github.com/prebid/prebid-server/v2@v2.18.0/exchange/utils_test.go (about)

     1  package exchange
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"sort"
     9  	"testing"
    10  
    11  	"github.com/prebid/prebid-server/v2/stored_responses"
    12  
    13  	gpplib "github.com/prebid/go-gpp"
    14  	"github.com/prebid/go-gpp/constants"
    15  	"github.com/prebid/openrtb/v20/openrtb2"
    16  	"github.com/prebid/prebid-server/v2/config"
    17  	"github.com/prebid/prebid-server/v2/errortypes"
    18  	"github.com/prebid/prebid-server/v2/firstpartydata"
    19  	"github.com/prebid/prebid-server/v2/gdpr"
    20  	"github.com/prebid/prebid-server/v2/metrics"
    21  	"github.com/prebid/prebid-server/v2/openrtb_ext"
    22  	"github.com/prebid/prebid-server/v2/privacy"
    23  	"github.com/prebid/prebid-server/v2/util/jsonutil"
    24  	"github.com/prebid/prebid-server/v2/util/ptrutil"
    25  	"github.com/stretchr/testify/assert"
    26  	"github.com/stretchr/testify/mock"
    27  	"github.com/stretchr/testify/require"
    28  )
    29  
    30  const deviceUA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36"
    31  
    32  // permissionsMock mocks the Permissions interface for tests
    33  type permissionsMock struct {
    34  	allowAllBidders bool
    35  	allowedBidders  []openrtb_ext.BidderName
    36  	passGeo         bool
    37  	passID          bool
    38  	activitiesError error
    39  }
    40  
    41  func (p *permissionsMock) HostCookiesAllowed(ctx context.Context) (bool, error) {
    42  	return true, nil
    43  }
    44  
    45  func (p *permissionsMock) BidderSyncAllowed(ctx context.Context, bidder openrtb_ext.BidderName) (bool, error) {
    46  	return true, nil
    47  }
    48  
    49  func (p *permissionsMock) AuctionActivitiesAllowed(ctx context.Context, bidderCoreName openrtb_ext.BidderName, bidder openrtb_ext.BidderName) (gdpr.AuctionPermissions, error) {
    50  	permissions := gdpr.AuctionPermissions{
    51  		PassGeo: p.passGeo,
    52  		PassID:  p.passID,
    53  	}
    54  
    55  	if p.allowAllBidders {
    56  		permissions.AllowBidRequest = true
    57  		return permissions, p.activitiesError
    58  	}
    59  
    60  	for _, allowedBidder := range p.allowedBidders {
    61  		if bidder == allowedBidder {
    62  			permissions.AllowBidRequest = true
    63  		}
    64  	}
    65  
    66  	return permissions, p.activitiesError
    67  }
    68  
    69  type fakePermissionsBuilder struct {
    70  	permissions gdpr.Permissions
    71  }
    72  
    73  func (fpb fakePermissionsBuilder) Builder(gdpr.TCF2ConfigReader, gdpr.RequestInfo) gdpr.Permissions {
    74  	return fpb.permissions
    75  }
    76  
    77  func assertReq(t *testing.T, bidderRequests []BidderRequest,
    78  	applyCOPPA bool, consentedVendors map[string]bool) {
    79  	// assert individual bidder requests
    80  	assert.NotEqual(t, bidderRequests, 0, "cleanOpenRTBRequest should split request into individual bidder requests")
    81  
    82  	// assert for PI data
    83  	for _, req := range bidderRequests {
    84  		if !applyCOPPA && consentedVendors[req.BidderName.String()] {
    85  			assert.NotEqual(t, req.BidRequest.User.BuyerUID, "", "cleanOpenRTBRequest shouldn't clean PI data as per COPPA or for a consented vendor as per GDPR or per CCPA")
    86  			assert.NotEqual(t, req.BidRequest.Device.DIDMD5, "", "cleanOpenRTBRequest shouldn't clean PI data as per COPPA or for a consented vendor as per GDPR or per CCPA")
    87  		} else {
    88  			assert.Equal(t, req.BidRequest.User.BuyerUID, "", "cleanOpenRTBRequest should clean PI data as per COPPA or for a non-consented vendor as per GDPR or per CCPA", req.BidderName.String())
    89  			assert.Equal(t, req.BidRequest.Device.DIDMD5, "", "cleanOpenRTBRequest should clean PI data as per COPPA or for a non-consented vendor as per GDPR or per CCPA", req.BidderName.String())
    90  		}
    91  	}
    92  }
    93  
    94  func TestSplitImps(t *testing.T) {
    95  	testCases := []struct {
    96  		description   string
    97  		givenImps     []openrtb2.Imp
    98  		expectedImps  map[string][]openrtb2.Imp
    99  		expectedError string
   100  	}{
   101  		{
   102  			description:   "Nil",
   103  			givenImps:     nil,
   104  			expectedImps:  map[string][]openrtb2.Imp{},
   105  			expectedError: "",
   106  		},
   107  		{
   108  			description:   "Empty",
   109  			givenImps:     []openrtb2.Imp{},
   110  			expectedImps:  map[string][]openrtb2.Imp{},
   111  			expectedError: "",
   112  		},
   113  		{
   114  			description: "1 Imp, 1 Bidder",
   115  			givenImps: []openrtb2.Imp{
   116  				{ID: "imp1", Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"imp1ParamA":"imp1ValueA"}}}}`)},
   117  			},
   118  			expectedImps: map[string][]openrtb2.Imp{
   119  				"bidderA": {
   120  					{ID: "imp1", Ext: json.RawMessage(`{"bidder":{"imp1ParamA":"imp1ValueA"}}`)},
   121  				},
   122  			},
   123  			expectedError: "",
   124  		},
   125  		{
   126  			description: "1 Imp, 2 Bidders",
   127  			givenImps: []openrtb2.Imp{
   128  				{ID: "imp1", Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"imp1ParamA":"imp1ValueA"},"bidderB":{"imp1ParamB":"imp1ValueB"}}}}`)},
   129  			},
   130  			expectedImps: map[string][]openrtb2.Imp{
   131  				"bidderA": {
   132  					{ID: "imp1", Ext: json.RawMessage(`{"bidder":{"imp1ParamA":"imp1ValueA"}}`)},
   133  				},
   134  				"bidderB": {
   135  					{ID: "imp1", Ext: json.RawMessage(`{"bidder":{"imp1ParamB":"imp1ValueB"}}`)},
   136  				},
   137  			},
   138  			expectedError: "",
   139  		},
   140  		{
   141  			description: "2 Imps, 1 Bidders Each",
   142  			givenImps: []openrtb2.Imp{
   143  				{ID: "imp1", Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"imp1ParamA":"imp1ValueA"}}}}`)},
   144  				{ID: "imp2", Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"imp2ParamA":"imp2ValueA"}}}}`)},
   145  			},
   146  			expectedImps: map[string][]openrtb2.Imp{
   147  				"bidderA": {
   148  					{ID: "imp1", Ext: json.RawMessage(`{"bidder":{"imp1ParamA":"imp1ValueA"}}`)},
   149  					{ID: "imp2", Ext: json.RawMessage(`{"bidder":{"imp2ParamA":"imp2ValueA"}}`)},
   150  				},
   151  			},
   152  			expectedError: "",
   153  		},
   154  		{
   155  			description: "2 Imps, 2 Bidders Each",
   156  			givenImps: []openrtb2.Imp{
   157  				{ID: "imp1", Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"imp1paramA":"imp1valueA"},"bidderB":{"imp1paramB":"imp1valueB"}}}}`)},
   158  				{ID: "imp2", Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"imp2paramA":"imp2valueA"},"bidderB":{"imp2paramB":"imp2valueB"}}}}`)},
   159  			},
   160  			expectedImps: map[string][]openrtb2.Imp{
   161  				"bidderA": {
   162  					{ID: "imp1", Ext: json.RawMessage(`{"bidder":{"imp1paramA":"imp1valueA"}}`)},
   163  					{ID: "imp2", Ext: json.RawMessage(`{"bidder":{"imp2paramA":"imp2valueA"}}`)},
   164  				},
   165  				"bidderB": {
   166  					{ID: "imp1", Ext: json.RawMessage(`{"bidder":{"imp1paramB":"imp1valueB"}}`)},
   167  					{ID: "imp2", Ext: json.RawMessage(`{"bidder":{"imp2paramB":"imp2valueB"}}`)},
   168  				},
   169  			},
   170  			expectedError: "",
   171  		},
   172  		{
   173  			// This is a "happy path" integration test. Functionality is covered in detail by TestCreateSanitizedImpExt.
   174  			description: "Other Fields - 2 Imps, 2 Bidders Each",
   175  			givenImps: []openrtb2.Imp{
   176  				{ID: "imp1", Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"imp1paramA":"imp1valueA"},"bidderB":{"imp1paramB":"imp1valueB"}}},"skadn":"imp1SkAdN"}`)},
   177  				{ID: "imp2", Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"imp2paramA":"imp2valueA"},"bidderB":{"imp2paramB":"imp2valueB"}}},"skadn":"imp2SkAdN"}`)},
   178  			},
   179  			expectedImps: map[string][]openrtb2.Imp{
   180  				"bidderA": {
   181  					{ID: "imp1", Ext: json.RawMessage(`{"bidder":{"imp1paramA":"imp1valueA"},"skadn":"imp1SkAdN"}`)},
   182  					{ID: "imp2", Ext: json.RawMessage(`{"bidder":{"imp2paramA":"imp2valueA"},"skadn":"imp2SkAdN"}`)},
   183  				},
   184  				"bidderB": {
   185  					{ID: "imp1", Ext: json.RawMessage(`{"bidder":{"imp1paramB":"imp1valueB"},"skadn":"imp1SkAdN"}`)},
   186  					{ID: "imp2", Ext: json.RawMessage(`{"bidder":{"imp2paramB":"imp2valueB"},"skadn":"imp2SkAdN"}`)},
   187  				},
   188  			},
   189  			expectedError: "",
   190  		},
   191  		{
   192  			description: "Malformed imp.ext",
   193  			givenImps: []openrtb2.Imp{
   194  				{ID: "imp1", Ext: json.RawMessage(`malformed`)},
   195  			},
   196  			expectedError: "invalid json for imp[0]: expect { or n, but found m",
   197  		},
   198  		{
   199  			description: "Malformed imp.ext.prebid",
   200  			givenImps: []openrtb2.Imp{
   201  				{ID: "imp1", Ext: json.RawMessage(`{"prebid": malformed}`)},
   202  			},
   203  			expectedError: "invalid json for imp[0]: do not know how to skip: 109",
   204  		},
   205  		{
   206  			description: "Malformed imp.ext.prebid.bidder",
   207  			givenImps: []openrtb2.Imp{
   208  				{ID: "imp1", Ext: json.RawMessage(`{"prebid": {"bidder": malformed}}`)},
   209  			},
   210  			expectedError: "invalid json for imp[0]: do not know how to skip: 109",
   211  		},
   212  	}
   213  
   214  	for _, test := range testCases {
   215  		imps, err := splitImps(test.givenImps)
   216  
   217  		if test.expectedError == "" {
   218  			assert.NoError(t, err, test.description+":err")
   219  		} else {
   220  			assert.EqualError(t, err, test.expectedError, test.description+":err")
   221  		}
   222  
   223  		assert.Equal(t, test.expectedImps, imps, test.description+":imps")
   224  	}
   225  }
   226  
   227  func TestCreateSanitizedImpExt(t *testing.T) {
   228  	testCases := []struct {
   229  		description       string
   230  		givenImpExt       map[string]json.RawMessage
   231  		givenImpExtPrebid map[string]json.RawMessage
   232  		expected          map[string]json.RawMessage
   233  		expectedError     string
   234  	}{
   235  		{
   236  			description:       "Nil",
   237  			givenImpExt:       nil,
   238  			givenImpExtPrebid: nil,
   239  			expected:          map[string]json.RawMessage{},
   240  			expectedError:     "",
   241  		},
   242  		{
   243  			description:       "Empty",
   244  			givenImpExt:       map[string]json.RawMessage{},
   245  			givenImpExtPrebid: map[string]json.RawMessage{},
   246  			expected:          map[string]json.RawMessage{},
   247  			expectedError:     "",
   248  		},
   249  		{
   250  			description: "imp.ext.prebid - Bidder Only",
   251  			givenImpExt: map[string]json.RawMessage{
   252  				"prebid":  json.RawMessage(`"ignoredInFavorOfSeparatelyUnmarshalledImpExtPrebid"`),
   253  				"data":    json.RawMessage(`"anyData"`),
   254  				"context": json.RawMessage(`"anyContext"`),
   255  				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
   256  				"gpid":    json.RawMessage(`"anyGPID"`),
   257  				"tid":     json.RawMessage(`"anyTID"`),
   258  			},
   259  			givenImpExtPrebid: map[string]json.RawMessage{
   260  				"bidder": json.RawMessage(`"anyBidder"`),
   261  			},
   262  			expected: map[string]json.RawMessage{
   263  				"data":    json.RawMessage(`"anyData"`),
   264  				"context": json.RawMessage(`"anyContext"`),
   265  				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
   266  				"gpid":    json.RawMessage(`"anyGPID"`),
   267  				"tid":     json.RawMessage(`"anyTID"`),
   268  			},
   269  			expectedError: "",
   270  		},
   271  		{
   272  			description: "imp.ext.prebid - Bidder + Other Forbidden Value",
   273  			givenImpExt: map[string]json.RawMessage{
   274  				"prebid":  json.RawMessage(`"ignoredInFavorOfSeparatelyUnmarshalledImpExtPrebid"`),
   275  				"data":    json.RawMessage(`"anyData"`),
   276  				"context": json.RawMessage(`"anyContext"`),
   277  				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
   278  				"gpid":    json.RawMessage(`"anyGPID"`),
   279  				"tid":     json.RawMessage(`"anyTID"`),
   280  			},
   281  			givenImpExtPrebid: map[string]json.RawMessage{
   282  				"bidder":    json.RawMessage(`"anyBidder"`),
   283  				"forbidden": json.RawMessage(`"anyValue"`),
   284  			},
   285  			expected: map[string]json.RawMessage{
   286  				"data":    json.RawMessage(`"anyData"`),
   287  				"context": json.RawMessage(`"anyContext"`),
   288  				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
   289  				"gpid":    json.RawMessage(`"anyGPID"`),
   290  				"tid":     json.RawMessage(`"anyTID"`),
   291  			},
   292  			expectedError: "",
   293  		},
   294  		{
   295  			description: "imp.ext.prebid - Bidder + Other Allowed Values",
   296  			givenImpExt: map[string]json.RawMessage{
   297  				"prebid":  json.RawMessage(`"ignoredInFavorOfSeparatelyUnmarshalledImpExtPrebid"`),
   298  				"data":    json.RawMessage(`"anyData"`),
   299  				"context": json.RawMessage(`"anyContext"`),
   300  				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
   301  				"gpid":    json.RawMessage(`"anyGPID"`),
   302  				"tid":     json.RawMessage(`"anyTID"`),
   303  			},
   304  			givenImpExtPrebid: map[string]json.RawMessage{
   305  				"bidder":                json.RawMessage(`"anyBidder"`),
   306  				"is_rewarded_inventory": json.RawMessage(`"anyIsRewardedInventory"`),
   307  				"options":               json.RawMessage(`"anyOptions"`),
   308  			},
   309  			expected: map[string]json.RawMessage{
   310  				"prebid":  json.RawMessage(`{"is_rewarded_inventory":"anyIsRewardedInventory","options":"anyOptions"}`),
   311  				"data":    json.RawMessage(`"anyData"`),
   312  				"context": json.RawMessage(`"anyContext"`),
   313  				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
   314  				"gpid":    json.RawMessage(`"anyGPID"`),
   315  				"tid":     json.RawMessage(`"anyTID"`),
   316  			},
   317  			expectedError: "",
   318  		},
   319  		{
   320  			description: "imp.ext",
   321  			givenImpExt: map[string]json.RawMessage{
   322  				"anyBidder": json.RawMessage(`"anyBidderValues"`),
   323  				"data":      json.RawMessage(`"anyData"`),
   324  				"context":   json.RawMessage(`"anyContext"`),
   325  				"skadn":     json.RawMessage(`"anySKAdNetwork"`),
   326  				"gpid":      json.RawMessage(`"anyGPID"`),
   327  				"tid":       json.RawMessage(`"anyTID"`),
   328  			},
   329  			givenImpExtPrebid: map[string]json.RawMessage{},
   330  			expected: map[string]json.RawMessage{
   331  				"data":    json.RawMessage(`"anyData"`),
   332  				"context": json.RawMessage(`"anyContext"`),
   333  				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
   334  				"gpid":    json.RawMessage(`"anyGPID"`),
   335  				"tid":     json.RawMessage(`"anyTID"`),
   336  			},
   337  			expectedError: "",
   338  		},
   339  		{
   340  			description: "imp.ext + imp.ext.prebid - Prebid Bidder Only",
   341  			givenImpExt: map[string]json.RawMessage{
   342  				"anyBidder": json.RawMessage(`"anyBidderValues"`),
   343  				"prebid":    json.RawMessage(`"ignoredInFavorOfSeparatelyUnmarshalledImpExtPrebid"`),
   344  				"data":      json.RawMessage(`"anyData"`),
   345  				"context":   json.RawMessage(`"anyContext"`),
   346  				"skadn":     json.RawMessage(`"anySKAdNetwork"`),
   347  				"gpid":      json.RawMessage(`"anyGPID"`),
   348  				"tid":       json.RawMessage(`"anyTID"`),
   349  			},
   350  			givenImpExtPrebid: map[string]json.RawMessage{
   351  				"bidder": json.RawMessage(`"anyBidder"`),
   352  			},
   353  			expected: map[string]json.RawMessage{
   354  				"data":    json.RawMessage(`"anyData"`),
   355  				"context": json.RawMessage(`"anyContext"`),
   356  				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
   357  				"gpid":    json.RawMessage(`"anyGPID"`),
   358  				"tid":     json.RawMessage(`"anyTID"`),
   359  			},
   360  			expectedError: "",
   361  		},
   362  		{
   363  			description: "imp.ext + imp.ext.prebid - Prebid Bidder + Other Forbidden Value",
   364  			givenImpExt: map[string]json.RawMessage{
   365  				"anyBidder": json.RawMessage(`"anyBidderValues"`),
   366  				"prebid":    json.RawMessage(`"ignoredInFavorOfSeparatelyUnmarshalledImpExtPrebid"`),
   367  				"data":      json.RawMessage(`"anyData"`),
   368  				"context":   json.RawMessage(`"anyContext"`),
   369  				"skadn":     json.RawMessage(`"anySKAdNetwork"`),
   370  				"gpid":      json.RawMessage(`"anyGPID"`),
   371  				"tid":       json.RawMessage(`"anyTID"`),
   372  			},
   373  			givenImpExtPrebid: map[string]json.RawMessage{
   374  				"bidder":    json.RawMessage(`"anyBidder"`),
   375  				"forbidden": json.RawMessage(`"anyValue"`),
   376  			},
   377  			expected: map[string]json.RawMessage{
   378  				"data":    json.RawMessage(`"anyData"`),
   379  				"context": json.RawMessage(`"anyContext"`),
   380  				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
   381  				"gpid":    json.RawMessage(`"anyGPID"`),
   382  				"tid":     json.RawMessage(`"anyTID"`),
   383  			},
   384  			expectedError: "",
   385  		},
   386  		{
   387  			description: "imp.ext + imp.ext.prebid - Prebid Bidder + Other Allowed Values",
   388  			givenImpExt: map[string]json.RawMessage{
   389  				"anyBidder": json.RawMessage(`"anyBidderValues"`),
   390  				"prebid":    json.RawMessage(`"ignoredInFavorOfSeparatelyUnmarshalledImpExtPrebid"`),
   391  				"data":      json.RawMessage(`"anyData"`),
   392  				"context":   json.RawMessage(`"anyContext"`),
   393  				"skadn":     json.RawMessage(`"anySKAdNetwork"`),
   394  				"gpid":      json.RawMessage(`"anyGPID"`),
   395  				"tid":       json.RawMessage(`"anyTID"`),
   396  			},
   397  			givenImpExtPrebid: map[string]json.RawMessage{
   398  				"bidder":                json.RawMessage(`"anyBidder"`),
   399  				"is_rewarded_inventory": json.RawMessage(`"anyIsRewardedInventory"`),
   400  				"options":               json.RawMessage(`"anyOptions"`),
   401  			},
   402  			expected: map[string]json.RawMessage{
   403  				"prebid":  json.RawMessage(`{"is_rewarded_inventory":"anyIsRewardedInventory","options":"anyOptions"}`),
   404  				"data":    json.RawMessage(`"anyData"`),
   405  				"context": json.RawMessage(`"anyContext"`),
   406  				"skadn":   json.RawMessage(`"anySKAdNetwork"`),
   407  				"gpid":    json.RawMessage(`"anyGPID"`),
   408  				"tid":     json.RawMessage(`"anyTID"`),
   409  			},
   410  			expectedError: "",
   411  		},
   412  	}
   413  
   414  	for _, test := range testCases {
   415  		result, err := createSanitizedImpExt(test.givenImpExt, test.givenImpExtPrebid)
   416  
   417  		if test.expectedError == "" {
   418  			assert.NoError(t, err, test.description+":err")
   419  		} else {
   420  			assert.EqualError(t, err, test.expectedError, test.description+":err")
   421  		}
   422  
   423  		assert.Equal(t, test.expected, result, test.description)
   424  	}
   425  }
   426  
   427  func TestCleanOpenRTBRequests(t *testing.T) {
   428  	emptyTCF2Config := gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{})
   429  
   430  	testCases := []struct {
   431  		req              AuctionRequest
   432  		bidReqAssertions func(t *testing.T, bidderRequests []BidderRequest,
   433  			applyCOPPA bool, consentedVendors map[string]bool)
   434  		hasError         bool
   435  		applyCOPPA       bool
   436  		consentedVendors map[string]bool
   437  	}{
   438  		{
   439  			req:              AuctionRequest{BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: getTestBuildRequest(t)}, UserSyncs: &emptyUsersync{}, TCF2Config: emptyTCF2Config},
   440  			bidReqAssertions: assertReq,
   441  			hasError:         false,
   442  			applyCOPPA:       true,
   443  			consentedVendors: map[string]bool{"appnexus": true},
   444  		},
   445  		{
   446  			req:              AuctionRequest{BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: newAdapterAliasBidRequest(t)}, UserSyncs: &emptyUsersync{}, TCF2Config: emptyTCF2Config},
   447  			bidReqAssertions: assertReq,
   448  			hasError:         false,
   449  			applyCOPPA:       false,
   450  			consentedVendors: map[string]bool{"appnexus": true},
   451  		},
   452  	}
   453  
   454  	privacyConfig := config.Privacy{
   455  		CCPA: config.CCPA{
   456  			Enforce: true,
   457  		},
   458  		LMT: config.LMT{
   459  			Enforce: true,
   460  		},
   461  	}
   462  
   463  	for _, test := range testCases {
   464  
   465  		gdprPermsBuilder := fakePermissionsBuilder{
   466  			permissions: &permissionsMock{
   467  				allowAllBidders: true,
   468  			},
   469  		}.Builder
   470  
   471  		reqSplitter := &requestSplitter{
   472  			bidderToSyncerKey: map[string]string{},
   473  			me:                &metrics.MetricsEngineMock{},
   474  			privacyConfig:     privacyConfig,
   475  			gdprPermsBuilder:  gdprPermsBuilder,
   476  			hostSChainNode:    nil,
   477  			bidderInfo:        config.BidderInfos{},
   478  		}
   479  		bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo, false, map[string]float64{})
   480  		if test.hasError {
   481  			assert.NotNil(t, err, "Error shouldn't be nil")
   482  		} else {
   483  			assert.Nil(t, err, "Err should be nil")
   484  			test.bidReqAssertions(t, bidderRequests, test.applyCOPPA, test.consentedVendors)
   485  		}
   486  	}
   487  }
   488  
   489  func TestCleanOpenRTBRequestsWithFPD(t *testing.T) {
   490  	fpd := make(map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData)
   491  
   492  	apnFpd := firstpartydata.ResolvedFirstPartyData{
   493  		Site: &openrtb2.Site{Name: "fpdApnSite"},
   494  		App:  &openrtb2.App{Name: "fpdApnApp"},
   495  		User: &openrtb2.User{Keywords: "fpdApnUser"},
   496  	}
   497  	fpd[openrtb_ext.BidderName("rubicon")] = &apnFpd
   498  
   499  	emptyTCF2Config := gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{})
   500  
   501  	testCases := []struct {
   502  		description string
   503  		req         AuctionRequest
   504  		fpdExpected bool
   505  	}{
   506  		{
   507  			description: "Pass valid FPD data for bidder not found in the request",
   508  			req:         AuctionRequest{BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: getTestBuildRequest(t)}, UserSyncs: &emptyUsersync{}, FirstPartyData: fpd, TCF2Config: emptyTCF2Config},
   509  			fpdExpected: false,
   510  		},
   511  		{
   512  			description: "Pass valid FPD data for bidders specified in request",
   513  			req:         AuctionRequest{BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: newAdapterAliasBidRequest(t)}, UserSyncs: &emptyUsersync{}, FirstPartyData: fpd, TCF2Config: emptyTCF2Config},
   514  			fpdExpected: true,
   515  		},
   516  		{
   517  			description: "Bidders specified in request but there is no fpd data for this bidder",
   518  			req:         AuctionRequest{BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: newAdapterAliasBidRequest(t)}, UserSyncs: &emptyUsersync{}, FirstPartyData: make(map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData), TCF2Config: emptyTCF2Config},
   519  			fpdExpected: false,
   520  		},
   521  		{
   522  			description: "No FPD data passed",
   523  			req:         AuctionRequest{BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: newAdapterAliasBidRequest(t)}, UserSyncs: &emptyUsersync{}, FirstPartyData: nil, TCF2Config: emptyTCF2Config},
   524  			fpdExpected: false,
   525  		},
   526  	}
   527  
   528  	for _, test := range testCases {
   529  
   530  		gdprPermissionsBuilder := fakePermissionsBuilder{
   531  			permissions: &permissionsMock{
   532  				allowAllBidders: true,
   533  			},
   534  		}.Builder
   535  
   536  		reqSplitter := &requestSplitter{
   537  			bidderToSyncerKey: map[string]string{},
   538  			me:                &metrics.MetricsEngineMock{},
   539  			privacyConfig:     config.Privacy{},
   540  			gdprPermsBuilder:  gdprPermissionsBuilder,
   541  			hostSChainNode:    nil,
   542  			bidderInfo:        config.BidderInfos{},
   543  		}
   544  
   545  		bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo, false, map[string]float64{})
   546  		assert.Empty(t, err, "No errors should be returned")
   547  		for _, bidderRequest := range bidderRequests {
   548  			bidderName := bidderRequest.BidderName
   549  			if test.fpdExpected {
   550  				assert.Equal(t, fpd[bidderName].Site.Name, bidderRequest.BidRequest.Site.Name, "Incorrect FPD site name")
   551  				assert.Equal(t, fpd[bidderName].App.Name, bidderRequest.BidRequest.App.Name, "Incorrect FPD app name")
   552  				assert.Equal(t, fpd[bidderName].User.Keywords, bidderRequest.BidRequest.User.Keywords, "Incorrect FPD user keywords")
   553  				assert.Equal(t, test.req.BidRequestWrapper.User.BuyerUID, bidderRequest.BidRequest.User.BuyerUID, "Incorrect FPD user buyerUID")
   554  			} else {
   555  				assert.Equal(t, "", bidderRequest.BidRequest.Site.Name, "Incorrect FPD site name")
   556  				assert.Equal(t, "", bidderRequest.BidRequest.User.Keywords, "Incorrect FPD user keywords")
   557  			}
   558  		}
   559  	}
   560  }
   561  
   562  func TestExtractAdapterReqBidderParamsMap(t *testing.T) {
   563  	tests := []struct {
   564  		name            string
   565  		givenBidRequest *openrtb2.BidRequest
   566  		want            map[string]json.RawMessage
   567  		wantErr         error
   568  	}{
   569  		{
   570  			name:            "nil req",
   571  			givenBidRequest: nil,
   572  			want:            nil,
   573  			wantErr:         errors.New("error bidRequest should not be nil"),
   574  		},
   575  		{
   576  			name:            "nil req.ext",
   577  			givenBidRequest: &openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{}}`)},
   578  			want:            nil,
   579  			wantErr:         nil,
   580  		},
   581  		{
   582  			name:            "malformed req.ext",
   583  			givenBidRequest: &openrtb2.BidRequest{Ext: json.RawMessage("malformed")},
   584  			want:            nil,
   585  			wantErr:         errors.New("error decoding Request.ext : expect { or n, but found m"),
   586  		},
   587  		{
   588  			name:            "extract bidder params from req.Ext for input request in adapter code",
   589  			givenBidRequest: &openrtb2.BidRequest{Ext: json.RawMessage(`{"prebid":{"bidderparams": {"profile": 1234, "version": 1}}}`)},
   590  			want:            map[string]json.RawMessage{"profile": json.RawMessage(`1234`), "version": json.RawMessage(`1`)},
   591  			wantErr:         nil,
   592  		},
   593  	}
   594  	for _, tt := range tests {
   595  		t.Run(tt.name, func(t *testing.T) {
   596  			got, err := ExtractReqExtBidderParamsMap(tt.givenBidRequest)
   597  			assert.Equal(t, tt.wantErr, err, "err")
   598  			assert.Equal(t, tt.want, got, "result")
   599  		})
   600  	}
   601  }
   602  
   603  func TestCleanOpenRTBRequestsWithBidResponses(t *testing.T) {
   604  	bidRespId1 := json.RawMessage(`{"id": "resp_id1"}`)
   605  	bidRespId2 := json.RawMessage(`{"id": "resp_id2"}`)
   606  
   607  	testCases := []struct {
   608  		description            string
   609  		storedBidResponses     map[string]map[string]json.RawMessage
   610  		imps                   []openrtb2.Imp
   611  		expectedBidderRequests map[string]BidderRequest
   612  	}{
   613  		{
   614  			description: "Request with imp with one bidder stored bid response",
   615  			storedBidResponses: map[string]map[string]json.RawMessage{
   616  				"imp-id1": {"bidderA": bidRespId1},
   617  			},
   618  			imps: []openrtb2.Imp{
   619  				{
   620  					ID: "imp-id1",
   621  					Video: &openrtb2.Video{
   622  						W: ptrutil.ToPtr[int64](300),
   623  						H: ptrutil.ToPtr[int64](250),
   624  					},
   625  					Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"placementId":"123"}}}}`),
   626  				},
   627  			},
   628  			expectedBidderRequests: map[string]BidderRequest{
   629  				"bidderA": {
   630  					BidRequest: &openrtb2.BidRequest{Imp: nil},
   631  					BidderName: "bidderA",
   632  					BidderStoredResponses: map[string]json.RawMessage{
   633  						"imp-id1": bidRespId1},
   634  				},
   635  			},
   636  		},
   637  		{
   638  			description: "Request with imps with and without stored bid response for one bidder",
   639  			storedBidResponses: map[string]map[string]json.RawMessage{
   640  				"imp-id1": {"bidderA": bidRespId1},
   641  			},
   642  			imps: []openrtb2.Imp{
   643  				{
   644  					ID: "imp-id1",
   645  					Video: &openrtb2.Video{
   646  						W: ptrutil.ToPtr[int64](300),
   647  						H: ptrutil.ToPtr[int64](250),
   648  					},
   649  					Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"placementId":"123"}}}}`),
   650  				},
   651  				{
   652  					ID:  "imp-id2",
   653  					Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"placementId":"123"}}}}`),
   654  				},
   655  			},
   656  			expectedBidderRequests: map[string]BidderRequest{
   657  				"bidderA": {
   658  					BidRequest: &openrtb2.BidRequest{Imp: []openrtb2.Imp{
   659  						{ID: "imp-id2", Ext: json.RawMessage(`{"bidder":{"placementId":"123"}}`)},
   660  					}},
   661  					BidderName: "bidderA",
   662  					BidderStoredResponses: map[string]json.RawMessage{
   663  						"imp-id1": bidRespId1},
   664  				},
   665  			},
   666  		},
   667  		{
   668  			description: "Request with imp with 2 bidders stored bid response",
   669  			storedBidResponses: map[string]map[string]json.RawMessage{
   670  				"imp-id1": {"bidderA": bidRespId1, "bidderB": bidRespId2},
   671  			},
   672  			imps: []openrtb2.Imp{
   673  				{
   674  					ID: "imp-id1",
   675  					Video: &openrtb2.Video{
   676  						W: ptrutil.ToPtr[int64](300),
   677  						H: ptrutil.ToPtr[int64](250),
   678  					},
   679  					Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"placementId":"123"}}}}`),
   680  				},
   681  			},
   682  			expectedBidderRequests: map[string]BidderRequest{
   683  				"bidderA": {
   684  					BidRequest: &openrtb2.BidRequest{Imp: nil},
   685  					BidderName: "bidderA",
   686  					BidderStoredResponses: map[string]json.RawMessage{
   687  						"imp-id1": bidRespId1,
   688  					},
   689  				},
   690  				"bidderB": {
   691  					BidRequest: &openrtb2.BidRequest{Imp: nil},
   692  					BidderName: "bidderB",
   693  					BidderStoredResponses: map[string]json.RawMessage{
   694  						"imp-id1": bidRespId2},
   695  				},
   696  			},
   697  		},
   698  		{
   699  			description: "Request with 2 imps: with 2 bidders stored bid response and imp without stored responses",
   700  			storedBidResponses: map[string]map[string]json.RawMessage{
   701  				"imp-id1": {"bidderA": bidRespId1, "bidderB": bidRespId2},
   702  			},
   703  			imps: []openrtb2.Imp{
   704  				{
   705  					ID: "imp-id1",
   706  					Video: &openrtb2.Video{
   707  						W: ptrutil.ToPtr[int64](300),
   708  						H: ptrutil.ToPtr[int64](250),
   709  					},
   710  					Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"placementId":"123"}}}}`),
   711  				},
   712  				{
   713  					ID:  "imp-id2",
   714  					Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"placementId":"123"}}}}`),
   715  				},
   716  			},
   717  			expectedBidderRequests: map[string]BidderRequest{
   718  				"bidderA": {
   719  					BidRequest: &openrtb2.BidRequest{Imp: []openrtb2.Imp{
   720  						{ID: "imp-id2", Ext: json.RawMessage(`{"bidder":{"placementId":"123"}}`)},
   721  					}},
   722  					BidderName: "bidderA",
   723  					BidderStoredResponses: map[string]json.RawMessage{
   724  						"imp-id1": bidRespId1},
   725  				},
   726  				"bidderB": {
   727  					BidRequest: &openrtb2.BidRequest{Imp: nil},
   728  					BidderName: "bidderB",
   729  					BidderStoredResponses: map[string]json.RawMessage{
   730  						"imp-id1": bidRespId2},
   731  				},
   732  			},
   733  		},
   734  		{
   735  			description: "Request with 3 imps: with 2 bidders stored bid response and 2 imps without stored responses",
   736  			storedBidResponses: map[string]map[string]json.RawMessage{
   737  				"imp-id1": {"bidderA": bidRespId1, "bidderB": bidRespId2},
   738  			},
   739  			imps: []openrtb2.Imp{
   740  				{
   741  					ID:  "imp-id3",
   742  					Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderC":{"placementId":"1234"}}}}`),
   743  				},
   744  				{
   745  					ID: "imp-id1",
   746  					Video: &openrtb2.Video{
   747  						W: ptrutil.ToPtr[int64](300),
   748  						H: ptrutil.ToPtr[int64](250),
   749  					},
   750  					Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"placementId":"123"}}}}`),
   751  				},
   752  				{
   753  					ID:  "imp-id2",
   754  					Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"placementId":"123"}}}}`),
   755  				},
   756  			},
   757  			expectedBidderRequests: map[string]BidderRequest{
   758  				"bidderA": {
   759  					BidRequest: &openrtb2.BidRequest{Imp: []openrtb2.Imp{
   760  						{ID: "imp-id2", Ext: json.RawMessage(`{"bidder":{"placementId":"123"}}`)},
   761  					}},
   762  					BidderName: "bidderA",
   763  					BidderStoredResponses: map[string]json.RawMessage{
   764  						"imp-id1": bidRespId1},
   765  				},
   766  				"bidderB": {
   767  					BidRequest: &openrtb2.BidRequest{Imp: nil},
   768  					BidderName: "bidderB",
   769  					BidderStoredResponses: map[string]json.RawMessage{
   770  						"imp-id1": bidRespId2},
   771  				},
   772  				"bidderC": {
   773  					BidRequest: &openrtb2.BidRequest{Imp: []openrtb2.Imp{
   774  						{ID: "imp-id3", Ext: json.RawMessage(`{"bidder":{"placementId":"1234"}}`)},
   775  					}},
   776  					BidderName:            "bidderC",
   777  					BidderStoredResponses: nil,
   778  				},
   779  			},
   780  		},
   781  		{
   782  			description: "Request with 2 imps: with 1 bidders stored bid response and imp without stored responses and with the same bidder",
   783  			storedBidResponses: map[string]map[string]json.RawMessage{
   784  				"imp-id2": {"bidderA": bidRespId2},
   785  			},
   786  			imps: []openrtb2.Imp{
   787  				{
   788  					ID:  "imp-id1",
   789  					Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"placementId":"123"}}}}`),
   790  				},
   791  				{
   792  					ID:  "imp-id2",
   793  					Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"placementId":"123"}}}}`),
   794  				},
   795  			},
   796  			expectedBidderRequests: map[string]BidderRequest{
   797  				"bidderA": {
   798  					BidRequest: &openrtb2.BidRequest{Imp: []openrtb2.Imp{
   799  						{ID: "imp-id1", Ext: json.RawMessage(`{"bidder":{"placementId":"123"}}`)},
   800  					}},
   801  					BidderName: "bidderA",
   802  					BidderStoredResponses: map[string]json.RawMessage{
   803  						"imp-id2": bidRespId2},
   804  				},
   805  			},
   806  		},
   807  		{
   808  			description: "Request with 2 imps with stored responses and with the same bidder",
   809  			storedBidResponses: map[string]map[string]json.RawMessage{
   810  				"imp-id1": {"bidderA": bidRespId1},
   811  				"imp-id2": {"bidderA": bidRespId2},
   812  			},
   813  			imps: []openrtb2.Imp{
   814  				{
   815  					ID:  "imp-id1",
   816  					Ext: json.RawMessage(`"prebid": {}`),
   817  				},
   818  				{
   819  					ID:  "imp-id2",
   820  					Ext: json.RawMessage(`"prebid": {}`),
   821  				},
   822  			},
   823  			expectedBidderRequests: map[string]BidderRequest{
   824  				"bidderA": {
   825  					BidRequest: &openrtb2.BidRequest{Imp: nil},
   826  					BidderName: "bidderA",
   827  					BidderStoredResponses: map[string]json.RawMessage{
   828  						"imp-id1": bidRespId1,
   829  						"imp-id2": bidRespId2,
   830  					},
   831  				},
   832  			},
   833  		},
   834  	}
   835  
   836  	for _, test := range testCases {
   837  
   838  		gdprPermissionsBuilder := fakePermissionsBuilder{
   839  			permissions: &permissionsMock{
   840  				allowAllBidders: true,
   841  			},
   842  		}.Builder
   843  
   844  		auctionReq := AuctionRequest{
   845  			BidRequestWrapper:  &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{Imp: test.imps}},
   846  			UserSyncs:          &emptyUsersync{},
   847  			StoredBidResponses: test.storedBidResponses,
   848  			TCF2Config:         gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
   849  		}
   850  
   851  		reqSplitter := &requestSplitter{
   852  			bidderToSyncerKey: map[string]string{},
   853  			me:                &metrics.MetricsEngineMock{},
   854  			privacyConfig:     config.Privacy{},
   855  			gdprPermsBuilder:  gdprPermissionsBuilder,
   856  			hostSChainNode:    nil,
   857  			bidderInfo:        config.BidderInfos{},
   858  		}
   859  
   860  		actualBidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, false, map[string]float64{})
   861  		assert.Empty(t, err, "No errors should be returned")
   862  		assert.Len(t, actualBidderRequests, len(test.expectedBidderRequests), "result len doesn't match for testCase %s", test.description)
   863  		for _, actualBidderRequest := range actualBidderRequests {
   864  			bidderName := string(actualBidderRequest.BidderName)
   865  			assert.Equal(t, test.expectedBidderRequests[bidderName].BidRequest.Imp, actualBidderRequest.BidRequest.Imp, "incorrect Impressions for testCase %s", test.description)
   866  			assert.Equal(t, test.expectedBidderRequests[bidderName].BidderStoredResponses, actualBidderRequest.BidderStoredResponses, "incorrect Bidder Stored Responses for testCase %s", test.description)
   867  		}
   868  	}
   869  }
   870  
   871  func TestCleanOpenRTBRequestsCCPA(t *testing.T) {
   872  	trueValue, falseValue := true, false
   873  
   874  	testCases := []struct {
   875  		description         string
   876  		reqExt              json.RawMessage
   877  		ccpaConsent         string
   878  		ccpaHostEnabled     bool
   879  		ccpaAccountEnabled  *bool
   880  		expectDataScrub     bool
   881  		expectPrivacyLabels metrics.PrivacyLabels
   882  	}{
   883  		{
   884  			description:        "Feature Flags Enabled - Opt Out",
   885  			ccpaConsent:        "1-Y-",
   886  			ccpaHostEnabled:    true,
   887  			ccpaAccountEnabled: &trueValue,
   888  			expectDataScrub:    true,
   889  			expectPrivacyLabels: metrics.PrivacyLabels{
   890  				CCPAProvided: true,
   891  				CCPAEnforced: true,
   892  			},
   893  		},
   894  		{
   895  			description:        "Feature Flags Enabled - Opt In",
   896  			ccpaConsent:        "1-N-",
   897  			ccpaHostEnabled:    true,
   898  			ccpaAccountEnabled: &trueValue,
   899  			expectDataScrub:    false,
   900  			expectPrivacyLabels: metrics.PrivacyLabels{
   901  				CCPAProvided: true,
   902  				CCPAEnforced: false,
   903  			},
   904  		},
   905  		{
   906  			description:        "Feature Flags Enabled - No Sale Star - Doesn't Scrub",
   907  			reqExt:             json.RawMessage(`{"prebid":{"nosale":["*"]}}`),
   908  			ccpaConsent:        "1-Y-",
   909  			ccpaHostEnabled:    true,
   910  			ccpaAccountEnabled: &trueValue,
   911  			expectDataScrub:    false,
   912  			expectPrivacyLabels: metrics.PrivacyLabels{
   913  				CCPAProvided: true,
   914  				CCPAEnforced: false,
   915  			},
   916  		},
   917  		{
   918  			description:        "Feature Flags Enabled - No Sale Specific Bidder - Doesn't Scrub",
   919  			reqExt:             json.RawMessage(`{"prebid":{"nosale":["appnexus"]}}`),
   920  			ccpaConsent:        "1-Y-",
   921  			ccpaHostEnabled:    true,
   922  			ccpaAccountEnabled: &trueValue,
   923  			expectDataScrub:    false,
   924  			expectPrivacyLabels: metrics.PrivacyLabels{
   925  				CCPAProvided: true,
   926  				CCPAEnforced: true,
   927  			},
   928  		},
   929  		{
   930  			description:        "Feature Flags Enabled - No Sale Different Bidder - Scrubs",
   931  			reqExt:             json.RawMessage(`{"prebid":{"nosale":["rubicon"]}}`),
   932  			ccpaConsent:        "1-Y-",
   933  			ccpaHostEnabled:    true,
   934  			ccpaAccountEnabled: &trueValue,
   935  			expectDataScrub:    true,
   936  			expectPrivacyLabels: metrics.PrivacyLabels{
   937  				CCPAProvided: true,
   938  				CCPAEnforced: true,
   939  			},
   940  		},
   941  		{
   942  			description:        "Feature flags Account CCPA enabled, host CCPA disregarded - Opt Out",
   943  			ccpaConsent:        "1-Y-",
   944  			ccpaHostEnabled:    false,
   945  			ccpaAccountEnabled: &trueValue,
   946  			expectDataScrub:    true,
   947  			expectPrivacyLabels: metrics.PrivacyLabels{
   948  				CCPAProvided: true,
   949  				CCPAEnforced: true,
   950  			},
   951  		},
   952  		{
   953  			description:        "Feature flags Account CCPA disabled, host CCPA disregarded",
   954  			ccpaConsent:        "1-Y-",
   955  			ccpaHostEnabled:    true,
   956  			ccpaAccountEnabled: &falseValue,
   957  			expectDataScrub:    false,
   958  			expectPrivacyLabels: metrics.PrivacyLabels{
   959  				CCPAProvided: true,
   960  				CCPAEnforced: false,
   961  			},
   962  		},
   963  		{
   964  			description:        "Feature flags Account CCPA not specified, host CCPA enabled - Opt Out",
   965  			ccpaConsent:        "1-Y-",
   966  			ccpaHostEnabled:    true,
   967  			ccpaAccountEnabled: nil,
   968  			expectDataScrub:    true,
   969  			expectPrivacyLabels: metrics.PrivacyLabels{
   970  				CCPAProvided: true,
   971  				CCPAEnforced: true,
   972  			},
   973  		},
   974  		{
   975  			description:        "Feature flags Account CCPA not specified, host CCPA disabled",
   976  			ccpaConsent:        "1-Y-",
   977  			ccpaHostEnabled:    false,
   978  			ccpaAccountEnabled: nil,
   979  			expectDataScrub:    false,
   980  			expectPrivacyLabels: metrics.PrivacyLabels{
   981  				CCPAProvided: true,
   982  				CCPAEnforced: false,
   983  			},
   984  		},
   985  	}
   986  
   987  	for _, test := range testCases {
   988  		req := newBidRequest(t)
   989  		req.Ext = test.reqExt
   990  		req.Regs = &openrtb2.Regs{
   991  			Ext: json.RawMessage(`{"us_privacy":"` + test.ccpaConsent + `"}`),
   992  		}
   993  
   994  		privacyConfig := config.Privacy{
   995  			CCPA: config.CCPA{
   996  				Enforce: test.ccpaHostEnabled,
   997  			},
   998  		}
   999  
  1000  		accountConfig := config.Account{
  1001  			CCPA: config.AccountCCPA{
  1002  				Enabled: test.ccpaAccountEnabled,
  1003  			},
  1004  		}
  1005  
  1006  		auctionReq := AuctionRequest{
  1007  			BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: req},
  1008  			UserSyncs:         &emptyUsersync{},
  1009  			Account:           accountConfig,
  1010  			TCF2Config:        gdpr.NewTCF2Config(config.TCF2{}, accountConfig.GDPR),
  1011  		}
  1012  
  1013  		gdprPermissionsBuilder := fakePermissionsBuilder{
  1014  			permissions: &permissionsMock{
  1015  				allowAllBidders: true,
  1016  			},
  1017  		}.Builder
  1018  
  1019  		metricsMock := metrics.MetricsEngineMock{}
  1020  		metricsMock.Mock.On("RecordAdapterBuyerUIDScrubbed", mock.Anything).Return()
  1021  
  1022  		bidderToSyncerKey := map[string]string{}
  1023  		reqSplitter := &requestSplitter{
  1024  			bidderToSyncerKey: bidderToSyncerKey,
  1025  			me:                &metricsMock,
  1026  			privacyConfig:     privacyConfig,
  1027  			gdprPermsBuilder:  gdprPermissionsBuilder,
  1028  			hostSChainNode:    nil,
  1029  			bidderInfo:        config.BidderInfos{},
  1030  		}
  1031  
  1032  		bidderRequests, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, false, map[string]float64{})
  1033  		result := bidderRequests[0]
  1034  
  1035  		assert.Nil(t, errs)
  1036  		if test.expectDataScrub {
  1037  			assert.Equal(t, result.BidRequest.User.BuyerUID, "", test.description+":User.BuyerUID")
  1038  			assert.Equal(t, result.BidRequest.Device.DIDMD5, "", test.description+":Device.DIDMD5")
  1039  			metricsMock.AssertCalled(t, "RecordAdapterBuyerUIDScrubbed", openrtb_ext.BidderAppnexus)
  1040  		} else {
  1041  			assert.NotEqual(t, result.BidRequest.User.BuyerUID, "", test.description+":User.BuyerUID")
  1042  			assert.NotEqual(t, result.BidRequest.Device.DIDMD5, "", test.description+":Device.DIDMD5")
  1043  			metricsMock.AssertNotCalled(t, "RecordAdapterBuyerUIDScrubbed", openrtb_ext.BidderAppnexus)
  1044  		}
  1045  		assert.Equal(t, test.expectPrivacyLabels, privacyLabels, test.description+":PrivacyLabels")
  1046  	}
  1047  }
  1048  
  1049  func TestCleanOpenRTBRequestsCCPAErrors(t *testing.T) {
  1050  	testCases := []struct {
  1051  		description string
  1052  		reqExt      json.RawMessage
  1053  		reqRegsExt  json.RawMessage
  1054  		expectError error
  1055  	}{
  1056  		{
  1057  			description: "Invalid Consent",
  1058  			reqExt:      json.RawMessage(`{"prebid":{"nosale":["*"]}}`),
  1059  			reqRegsExt:  json.RawMessage(`{"us_privacy":"malformed"}`),
  1060  			expectError: &errortypes.Warning{
  1061  				Message:     "request.regs.ext.us_privacy must contain 4 characters",
  1062  				WarningCode: errortypes.InvalidPrivacyConsentWarningCode,
  1063  			},
  1064  		},
  1065  		{
  1066  			description: "Invalid No Sale Bidders",
  1067  			reqExt:      json.RawMessage(`{"prebid":{"nosale":["*", "another"]}}`),
  1068  			reqRegsExt:  json.RawMessage(`{"us_privacy":"1NYN"}`),
  1069  			expectError: errors.New("request.ext.prebid.nosale is invalid: can only specify all bidders if no other bidders are provided"),
  1070  		},
  1071  	}
  1072  
  1073  	for _, test := range testCases {
  1074  		req := newBidRequest(t)
  1075  		req.Ext = test.reqExt
  1076  		req.Regs = &openrtb2.Regs{Ext: test.reqRegsExt}
  1077  
  1078  		var reqExtStruct openrtb_ext.ExtRequest
  1079  		err := jsonutil.UnmarshalValid(req.Ext, &reqExtStruct)
  1080  		assert.NoError(t, err, test.description+":marshal_ext")
  1081  
  1082  		auctionReq := AuctionRequest{
  1083  			BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: req},
  1084  			UserSyncs:         &emptyUsersync{},
  1085  			TCF2Config:        gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
  1086  		}
  1087  
  1088  		gdprPermissionsBuilder := fakePermissionsBuilder{
  1089  			permissions: &permissionsMock{
  1090  				allowAllBidders: true,
  1091  			},
  1092  		}.Builder
  1093  
  1094  		privacyConfig := config.Privacy{
  1095  			CCPA: config.CCPA{
  1096  				Enforce: true,
  1097  			},
  1098  		}
  1099  		bidderToSyncerKey := map[string]string{}
  1100  		metrics := metrics.MetricsEngineMock{}
  1101  
  1102  		reqSplitter := &requestSplitter{
  1103  			bidderToSyncerKey: bidderToSyncerKey,
  1104  			me:                &metrics,
  1105  			privacyConfig:     privacyConfig,
  1106  			gdprPermsBuilder:  gdprPermissionsBuilder,
  1107  			hostSChainNode:    nil,
  1108  			bidderInfo:        config.BidderInfos{},
  1109  		}
  1110  
  1111  		_, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, &reqExtStruct, gdpr.SignalNo, false, map[string]float64{})
  1112  
  1113  		assert.ElementsMatch(t, []error{test.expectError}, errs, test.description)
  1114  	}
  1115  }
  1116  
  1117  func TestCleanOpenRTBRequestsCOPPA(t *testing.T) {
  1118  	testCases := []struct {
  1119  		description         string
  1120  		coppa               int8
  1121  		expectDataScrub     bool
  1122  		expectPrivacyLabels metrics.PrivacyLabels
  1123  	}{
  1124  		{
  1125  			description:     "Enabled",
  1126  			coppa:           1,
  1127  			expectDataScrub: true,
  1128  			expectPrivacyLabels: metrics.PrivacyLabels{
  1129  				COPPAEnforced: true,
  1130  			},
  1131  		},
  1132  		{
  1133  			description:     "Disabled",
  1134  			coppa:           0,
  1135  			expectDataScrub: false,
  1136  			expectPrivacyLabels: metrics.PrivacyLabels{
  1137  				COPPAEnforced: false,
  1138  			},
  1139  		},
  1140  	}
  1141  
  1142  	for _, test := range testCases {
  1143  		req := newBidRequest(t)
  1144  		req.Regs = &openrtb2.Regs{COPPA: test.coppa}
  1145  
  1146  		auctionReq := AuctionRequest{
  1147  			BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: req},
  1148  			UserSyncs:         &emptyUsersync{},
  1149  			TCF2Config:        gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
  1150  		}
  1151  
  1152  		gdprPermissionsBuilder := fakePermissionsBuilder{
  1153  			permissions: &permissionsMock{
  1154  				allowAllBidders: true,
  1155  			},
  1156  		}.Builder
  1157  
  1158  		bidderToSyncerKey := map[string]string{}
  1159  		metrics := metrics.MetricsEngineMock{}
  1160  
  1161  		reqSplitter := &requestSplitter{
  1162  			bidderToSyncerKey: bidderToSyncerKey,
  1163  			me:                &metrics,
  1164  			privacyConfig:     config.Privacy{},
  1165  			gdprPermsBuilder:  gdprPermissionsBuilder,
  1166  			hostSChainNode:    nil,
  1167  			bidderInfo:        config.BidderInfos{},
  1168  		}
  1169  
  1170  		bidderRequests, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, false, map[string]float64{})
  1171  		result := bidderRequests[0]
  1172  
  1173  		assert.Nil(t, errs)
  1174  		if test.expectDataScrub {
  1175  			assert.Equal(t, result.BidRequest.User.BuyerUID, "", test.description+":User.BuyerUID")
  1176  			assert.Equal(t, result.BidRequest.User.Yob, int64(0), test.description+":User.Yob")
  1177  		} else {
  1178  			assert.NotEqual(t, result.BidRequest.User.BuyerUID, "", test.description+":User.BuyerUID")
  1179  			assert.NotEqual(t, result.BidRequest.User.Yob, int64(0), test.description+":User.Yob")
  1180  		}
  1181  		assert.Equal(t, test.expectPrivacyLabels, privacyLabels, test.description+":PrivacyLabels")
  1182  	}
  1183  }
  1184  
  1185  func TestCleanOpenRTBRequestsSChain(t *testing.T) {
  1186  	const seller1SChain string = `"schain":{"complete":1,"nodes":[{"asi":"directseller1.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}`
  1187  	const seller2SChain string = `"schain":{"complete":2,"nodes":[{"asi":"directseller2.com","sid":"00002","rid":"BidRequest2","hp":2}],"ver":"2.0"}`
  1188  
  1189  	testCases := []struct {
  1190  		description   string
  1191  		inExt         json.RawMessage
  1192  		inSourceExt   json.RawMessage
  1193  		outRequestExt json.RawMessage
  1194  		outSourceExt  json.RawMessage
  1195  		hasError      bool
  1196  	}{
  1197  		{
  1198  			description:   "nil",
  1199  			inExt:         nil,
  1200  			inSourceExt:   nil,
  1201  			outRequestExt: nil,
  1202  			outSourceExt:  nil,
  1203  		},
  1204  		{
  1205  			description:   "ORTB 2.5 chain at source.ext.schain",
  1206  			inExt:         nil,
  1207  			inSourceExt:   json.RawMessage(`{` + seller1SChain + `}`),
  1208  			outRequestExt: nil,
  1209  			outSourceExt:  json.RawMessage(`{` + seller1SChain + `}`),
  1210  		},
  1211  		{
  1212  			description:   "ORTB 2.5 schain at request.ext.prebid.schains",
  1213  			inExt:         json.RawMessage(`{"prebid":{"schains":[{"bidders":["appnexus"],` + seller1SChain + `}]}}`),
  1214  			inSourceExt:   nil,
  1215  			outRequestExt: nil,
  1216  			outSourceExt:  json.RawMessage(`{` + seller1SChain + `}`),
  1217  		},
  1218  		{
  1219  			description:   "schainwriter instantation error -- multiple bidder schains in ext.prebid.schains.",
  1220  			inExt:         json.RawMessage(`{"prebid":{"schains":[{"bidders":["appnexus"],` + seller1SChain + `},{"bidders":["appnexus"],` + seller2SChain + `}]}}`),
  1221  			inSourceExt:   json.RawMessage(`{` + seller1SChain + `}`),
  1222  			outRequestExt: nil,
  1223  			outSourceExt:  nil,
  1224  			hasError:      true,
  1225  		},
  1226  	}
  1227  
  1228  	for _, test := range testCases {
  1229  		req := newBidRequest(t)
  1230  		if test.inSourceExt != nil {
  1231  			req.Source.Ext = test.inSourceExt
  1232  		}
  1233  
  1234  		var extRequest *openrtb_ext.ExtRequest
  1235  		if test.inExt != nil {
  1236  			req.Ext = test.inExt
  1237  			extRequest = &openrtb_ext.ExtRequest{}
  1238  			err := jsonutil.UnmarshalValid(req.Ext, extRequest)
  1239  			assert.NoErrorf(t, err, test.description+":Error unmarshaling inExt")
  1240  		}
  1241  
  1242  		auctionReq := AuctionRequest{
  1243  			BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: req},
  1244  			UserSyncs:         &emptyUsersync{},
  1245  			TCF2Config:        gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
  1246  		}
  1247  
  1248  		gdprPermissionsBuilder := fakePermissionsBuilder{
  1249  			permissions: &permissionsMock{
  1250  				allowAllBidders: true,
  1251  			},
  1252  		}.Builder
  1253  
  1254  		reqSplitter := &requestSplitter{
  1255  			bidderToSyncerKey: map[string]string{},
  1256  			me:                &metrics.MetricsEngineMock{},
  1257  			privacyConfig:     config.Privacy{},
  1258  			gdprPermsBuilder:  gdprPermissionsBuilder,
  1259  			hostSChainNode:    nil,
  1260  			bidderInfo:        config.BidderInfos{},
  1261  		}
  1262  
  1263  		bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo, false, map[string]float64{})
  1264  		if test.hasError == true {
  1265  			assert.NotNil(t, errs)
  1266  			assert.Len(t, bidderRequests, 0)
  1267  		} else {
  1268  			result := bidderRequests[0]
  1269  			assert.Nil(t, errs)
  1270  			assert.Equal(t, test.outSourceExt, result.BidRequest.Source.Ext, test.description+":Source.Ext")
  1271  			assert.Equal(t, test.outRequestExt, result.BidRequest.Ext, test.description+":Ext")
  1272  		}
  1273  	}
  1274  }
  1275  
  1276  func TestCleanOpenRTBRequestsBidderParams(t *testing.T) {
  1277  	testCases := []struct {
  1278  		description string
  1279  		inExt       json.RawMessage
  1280  		expectedExt map[string]json.RawMessage
  1281  		hasError    bool
  1282  	}{
  1283  		{
  1284  			description: "Nil Bidder params",
  1285  			inExt:       nil,
  1286  			expectedExt: getExpectedReqExt(true, false, false),
  1287  			hasError:    false,
  1288  		},
  1289  		{
  1290  			description: "Bidder params for single partner",
  1291  			inExt:       json.RawMessage(`{"prebid":{"bidderparams":{"pubmatic":{"profile":1234,"version":2}}}}`),
  1292  			expectedExt: getExpectedReqExt(false, true, false),
  1293  			hasError:    false,
  1294  		},
  1295  		{
  1296  			description: "Bidder params for two partners",
  1297  			inExt:       json.RawMessage(`{"prebid":{"bidderparams":{"pubmatic":{"profile":1234,"version":2},"appnexus":{"key1":123,"key2":{"innerKey1":"innerValue1"}}}}}`),
  1298  			expectedExt: getExpectedReqExt(false, true, true),
  1299  			hasError:    false,
  1300  		},
  1301  	}
  1302  
  1303  	for _, test := range testCases {
  1304  		req := newBidRequestWithBidderParams(t)
  1305  		var extRequest *openrtb_ext.ExtRequest
  1306  		if test.inExt != nil {
  1307  			req.Ext = test.inExt
  1308  			extRequest = &openrtb_ext.ExtRequest{}
  1309  			err := jsonutil.UnmarshalValid(req.Ext, extRequest)
  1310  			assert.NoErrorf(t, err, test.description+":Error unmarshaling inExt")
  1311  		}
  1312  
  1313  		auctionReq := AuctionRequest{
  1314  			BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: req},
  1315  			UserSyncs:         &emptyUsersync{},
  1316  			TCF2Config:        gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
  1317  		}
  1318  
  1319  		gdprPermissionsBuilder := fakePermissionsBuilder{
  1320  			permissions: &permissionsMock{
  1321  				allowAllBidders: true,
  1322  			},
  1323  		}.Builder
  1324  
  1325  		reqSplitter := &requestSplitter{
  1326  			bidderToSyncerKey: map[string]string{},
  1327  			me:                &metrics.MetricsEngineMock{},
  1328  			privacyConfig:     config.Privacy{},
  1329  			gdprPermsBuilder:  gdprPermissionsBuilder,
  1330  			hostSChainNode:    nil,
  1331  			bidderInfo:        config.BidderInfos{},
  1332  		}
  1333  
  1334  		bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo, false, map[string]float64{})
  1335  		if test.hasError == true {
  1336  			assert.NotNil(t, errs)
  1337  			assert.Len(t, bidderRequests, 0)
  1338  		} else {
  1339  			assert.Nil(t, errs)
  1340  			for _, r := range bidderRequests {
  1341  				expected := test.expectedExt[r.BidderName.String()]
  1342  				actual := r.BidRequest.Ext
  1343  				assert.Equal(t, expected, actual, test.description+" Req:Ext.Prebid.BidderParams")
  1344  			}
  1345  		}
  1346  	}
  1347  }
  1348  
  1349  func getExpectedReqExt(nilExt, includePubmaticParams, includeAppnexusParams bool) map[string]json.RawMessage {
  1350  	bidderParamsMap := make(map[string]json.RawMessage)
  1351  
  1352  	if nilExt {
  1353  		bidderParamsMap["pubmatic"] = nil
  1354  		bidderParamsMap["appnexus"] = nil
  1355  		return bidderParamsMap
  1356  	}
  1357  
  1358  	if includePubmaticParams {
  1359  		bidderParamsMap["pubmatic"] = json.RawMessage(`{"prebid":{"bidderparams":{"profile":1234,"version":2}}}`)
  1360  	} else {
  1361  		bidderParamsMap["pubmatic"] = nil
  1362  	}
  1363  
  1364  	if includeAppnexusParams {
  1365  		bidderParamsMap["appnexus"] = json.RawMessage(`{"prebid":{"bidderparams":{"key1":123,"key2":{"innerKey1":"innerValue1"}}}}`)
  1366  	} else {
  1367  		bidderParamsMap["appnexus"] = nil
  1368  	}
  1369  
  1370  	return bidderParamsMap
  1371  }
  1372  
  1373  func TestGetExtCacheInstructions(t *testing.T) {
  1374  	var boolFalse, boolTrue *bool = new(bool), new(bool)
  1375  	*boolFalse = false
  1376  	*boolTrue = true
  1377  
  1378  	testCases := []struct {
  1379  		desc                 string
  1380  		requestExtPrebid     *openrtb_ext.ExtRequestPrebid
  1381  		outCacheInstructions extCacheInstructions
  1382  	}{
  1383  		{
  1384  			desc:             "Nil request ext, all cache flags false except for returnCreative that defaults to true",
  1385  			requestExtPrebid: nil,
  1386  			outCacheInstructions: extCacheInstructions{
  1387  				cacheBids:      false,
  1388  				cacheVAST:      false,
  1389  				returnCreative: true,
  1390  			},
  1391  		},
  1392  		{
  1393  			desc: "Non-nil request ext, nil Cache field, all cache flags false except for returnCreative that defaults to true",
  1394  			requestExtPrebid: &openrtb_ext.ExtRequestPrebid{
  1395  				Cache: nil,
  1396  			},
  1397  			outCacheInstructions: extCacheInstructions{
  1398  				cacheBids:      false,
  1399  				cacheVAST:      false,
  1400  				returnCreative: true,
  1401  			},
  1402  		},
  1403  		{
  1404  			desc: "Non-nil Cache field, both ExtRequestPrebidCacheBids and ExtRequestPrebidCacheVAST nil returnCreative that defaults to true",
  1405  			requestExtPrebid: &openrtb_ext.ExtRequestPrebid{
  1406  				Cache: &openrtb_ext.ExtRequestPrebidCache{
  1407  					Bids:    nil,
  1408  					VastXML: nil,
  1409  				},
  1410  			},
  1411  			outCacheInstructions: extCacheInstructions{
  1412  				cacheBids:      false,
  1413  				cacheVAST:      false,
  1414  				returnCreative: true,
  1415  			},
  1416  		},
  1417  		{
  1418  			desc: "Non-nil ExtRequest.Cache.ExtRequestPrebidCacheVAST with unspecified ReturnCreative field, cacheVAST = true and returnCreative defaults to true",
  1419  			requestExtPrebid: &openrtb_ext.ExtRequestPrebid{
  1420  				Cache: &openrtb_ext.ExtRequestPrebidCache{
  1421  					Bids:    nil,
  1422  					VastXML: &openrtb_ext.ExtRequestPrebidCacheVAST{},
  1423  				},
  1424  			},
  1425  			outCacheInstructions: extCacheInstructions{
  1426  				cacheBids:      false,
  1427  				cacheVAST:      true,
  1428  				returnCreative: true, // default value
  1429  			},
  1430  		},
  1431  		{
  1432  			desc: "Non-nil ExtRequest.Cache.ExtRequestPrebidCacheVAST where ReturnCreative is set to false, cacheVAST = true and returnCreative = false",
  1433  			requestExtPrebid: &openrtb_ext.ExtRequestPrebid{
  1434  				Cache: &openrtb_ext.ExtRequestPrebidCache{
  1435  					Bids:    nil,
  1436  					VastXML: &openrtb_ext.ExtRequestPrebidCacheVAST{ReturnCreative: boolFalse},
  1437  				},
  1438  			},
  1439  			outCacheInstructions: extCacheInstructions{
  1440  				cacheBids:      false,
  1441  				cacheVAST:      true,
  1442  				returnCreative: false,
  1443  			},
  1444  		},
  1445  		{
  1446  			desc: "Non-nil ExtRequest.Cache.ExtRequestPrebidCacheVAST where ReturnCreative is set to true, cacheVAST = true and returnCreative = true",
  1447  			requestExtPrebid: &openrtb_ext.ExtRequestPrebid{
  1448  				Cache: &openrtb_ext.ExtRequestPrebidCache{
  1449  					Bids:    nil,
  1450  					VastXML: &openrtb_ext.ExtRequestPrebidCacheVAST{ReturnCreative: boolTrue},
  1451  				},
  1452  			},
  1453  			outCacheInstructions: extCacheInstructions{
  1454  				cacheBids:      false,
  1455  				cacheVAST:      true,
  1456  				returnCreative: true,
  1457  			},
  1458  		},
  1459  		{
  1460  			desc: "Non-nil ExtRequest.Cache.ExtRequestPrebidCacheBids with unspecified ReturnCreative field, cacheBids = true and returnCreative defaults to true",
  1461  			requestExtPrebid: &openrtb_ext.ExtRequestPrebid{
  1462  				Cache: &openrtb_ext.ExtRequestPrebidCache{
  1463  					Bids:    &openrtb_ext.ExtRequestPrebidCacheBids{},
  1464  					VastXML: nil,
  1465  				},
  1466  			},
  1467  			outCacheInstructions: extCacheInstructions{
  1468  				cacheBids:      true,
  1469  				cacheVAST:      false,
  1470  				returnCreative: true, // default value
  1471  			},
  1472  		},
  1473  		{
  1474  			desc: "Non-nil ExtRequest.Cache.ExtRequestPrebidCacheBids where ReturnCreative is set to false, cacheBids = true and returnCreative  = false",
  1475  			requestExtPrebid: &openrtb_ext.ExtRequestPrebid{
  1476  				Cache: &openrtb_ext.ExtRequestPrebidCache{
  1477  					Bids:    &openrtb_ext.ExtRequestPrebidCacheBids{ReturnCreative: boolFalse},
  1478  					VastXML: nil,
  1479  				},
  1480  			},
  1481  			outCacheInstructions: extCacheInstructions{
  1482  				cacheBids:      true,
  1483  				cacheVAST:      false,
  1484  				returnCreative: false,
  1485  			},
  1486  		},
  1487  		{
  1488  			desc: "Non-nil ExtRequest.Cache.ExtRequestPrebidCacheBids where ReturnCreative is set to true, cacheBids = true and returnCreative  = true",
  1489  			requestExtPrebid: &openrtb_ext.ExtRequestPrebid{
  1490  				Cache: &openrtb_ext.ExtRequestPrebidCache{
  1491  					Bids:    &openrtb_ext.ExtRequestPrebidCacheBids{ReturnCreative: boolTrue},
  1492  					VastXML: nil,
  1493  				},
  1494  			},
  1495  			outCacheInstructions: extCacheInstructions{
  1496  				cacheBids:      true,
  1497  				cacheVAST:      false,
  1498  				returnCreative: true,
  1499  			},
  1500  		},
  1501  		{
  1502  			desc: "Non-nil ExtRequest.Cache.ExtRequestPrebidCacheBids and ExtRequest.Cache.ExtRequestPrebidCacheVAST, neither specify a ReturnCreative field value, all extCacheInstructions fields set to true",
  1503  			requestExtPrebid: &openrtb_ext.ExtRequestPrebid{
  1504  				Cache: &openrtb_ext.ExtRequestPrebidCache{
  1505  					Bids:    &openrtb_ext.ExtRequestPrebidCacheBids{},
  1506  					VastXML: &openrtb_ext.ExtRequestPrebidCacheVAST{},
  1507  				},
  1508  			},
  1509  			outCacheInstructions: extCacheInstructions{
  1510  				cacheBids:      true,
  1511  				cacheVAST:      true,
  1512  				returnCreative: true,
  1513  			},
  1514  		},
  1515  		{
  1516  			desc: "Non-nil ExtRequest.Cache.ExtRequestPrebidCacheBids and ExtRequest.Cache.ExtRequestPrebidCacheVAST sets ReturnCreative to true, all extCacheInstructions fields set to true",
  1517  			requestExtPrebid: &openrtb_ext.ExtRequestPrebid{
  1518  				Cache: &openrtb_ext.ExtRequestPrebidCache{
  1519  					Bids:    &openrtb_ext.ExtRequestPrebidCacheBids{},
  1520  					VastXML: &openrtb_ext.ExtRequestPrebidCacheVAST{ReturnCreative: boolTrue},
  1521  				},
  1522  			},
  1523  			outCacheInstructions: extCacheInstructions{
  1524  				cacheBids:      true,
  1525  				cacheVAST:      true,
  1526  				returnCreative: true,
  1527  			},
  1528  		},
  1529  		{
  1530  			desc: "Non-nil ExtRequest.Cache.ExtRequestPrebidCacheBids and ExtRequest.Cache.ExtRequestPrebidCacheVAST sets ReturnCreative to false, returnCreative = false",
  1531  			requestExtPrebid: &openrtb_ext.ExtRequestPrebid{
  1532  				Cache: &openrtb_ext.ExtRequestPrebidCache{
  1533  					Bids:    &openrtb_ext.ExtRequestPrebidCacheBids{},
  1534  					VastXML: &openrtb_ext.ExtRequestPrebidCacheVAST{ReturnCreative: boolFalse},
  1535  				},
  1536  			},
  1537  			outCacheInstructions: extCacheInstructions{
  1538  				cacheBids:      true,
  1539  				cacheVAST:      true,
  1540  				returnCreative: false,
  1541  			},
  1542  		},
  1543  		{
  1544  			desc: "Non-nil ExtRequest.Cache.ExtRequestPrebidCacheVAST and ExtRequest.Cache.ExtRequestPrebidCacheBids sets ReturnCreative to true, all extCacheInstructions fields set to true",
  1545  			requestExtPrebid: &openrtb_ext.ExtRequestPrebid{
  1546  				Cache: &openrtb_ext.ExtRequestPrebidCache{
  1547  					Bids:    &openrtb_ext.ExtRequestPrebidCacheBids{ReturnCreative: boolTrue},
  1548  					VastXML: &openrtb_ext.ExtRequestPrebidCacheVAST{},
  1549  				},
  1550  			},
  1551  			outCacheInstructions: extCacheInstructions{
  1552  				cacheBids:      true,
  1553  				cacheVAST:      true,
  1554  				returnCreative: true,
  1555  			},
  1556  		},
  1557  		{
  1558  			desc: "Non-nil ExtRequest.Cache.ExtRequestPrebidCacheVAST and ExtRequest.Cache.ExtRequestPrebidCacheBids sets ReturnCreative to false, returnCreative = false",
  1559  			requestExtPrebid: &openrtb_ext.ExtRequestPrebid{
  1560  				Cache: &openrtb_ext.ExtRequestPrebidCache{
  1561  					Bids:    &openrtb_ext.ExtRequestPrebidCacheBids{ReturnCreative: boolFalse},
  1562  					VastXML: &openrtb_ext.ExtRequestPrebidCacheVAST{},
  1563  				},
  1564  			},
  1565  			outCacheInstructions: extCacheInstructions{
  1566  				cacheBids:      true,
  1567  				cacheVAST:      true,
  1568  				returnCreative: false,
  1569  			},
  1570  		},
  1571  		{
  1572  			desc: "Non-nil ExtRequest.Cache.ExtRequestPrebidCacheVAST and ExtRequest.Cache.ExtRequestPrebidCacheBids set different ReturnCreative values, returnCreative = true because one of them is true",
  1573  			requestExtPrebid: &openrtb_ext.ExtRequestPrebid{
  1574  				Cache: &openrtb_ext.ExtRequestPrebidCache{
  1575  					Bids:    &openrtb_ext.ExtRequestPrebidCacheBids{ReturnCreative: boolFalse},
  1576  					VastXML: &openrtb_ext.ExtRequestPrebidCacheVAST{ReturnCreative: boolTrue},
  1577  				},
  1578  			},
  1579  			outCacheInstructions: extCacheInstructions{
  1580  				cacheBids:      true,
  1581  				cacheVAST:      true,
  1582  				returnCreative: true,
  1583  			},
  1584  		},
  1585  		{
  1586  			desc: "Non-nil ExtRequest.Cache.ExtRequestPrebidCacheVAST and ExtRequest.Cache.ExtRequestPrebidCacheBids set different ReturnCreative values, returnCreative = true because one of them is true",
  1587  			requestExtPrebid: &openrtb_ext.ExtRequestPrebid{
  1588  				Cache: &openrtb_ext.ExtRequestPrebidCache{
  1589  					Bids:    &openrtb_ext.ExtRequestPrebidCacheBids{ReturnCreative: boolTrue},
  1590  					VastXML: &openrtb_ext.ExtRequestPrebidCacheVAST{ReturnCreative: boolFalse},
  1591  				},
  1592  			},
  1593  			outCacheInstructions: extCacheInstructions{
  1594  				cacheBids:      true,
  1595  				cacheVAST:      true,
  1596  				returnCreative: true,
  1597  			},
  1598  		},
  1599  	}
  1600  
  1601  	for _, test := range testCases {
  1602  		cacheInstructions := getExtCacheInstructions(test.requestExtPrebid)
  1603  
  1604  		assert.Equal(t, test.outCacheInstructions.cacheBids, cacheInstructions.cacheBids, "%s. Unexpected shouldCacheBids value. \n", test.desc)
  1605  		assert.Equal(t, test.outCacheInstructions.cacheVAST, cacheInstructions.cacheVAST, "%s. Unexpected shouldCacheVAST value. \n", test.desc)
  1606  		assert.Equal(t, test.outCacheInstructions.returnCreative, cacheInstructions.returnCreative, "%s. Unexpected returnCreative value. \n", test.desc)
  1607  	}
  1608  }
  1609  
  1610  func TestGetExtTargetData(t *testing.T) {
  1611  	type inTest struct {
  1612  		requestExtPrebid  *openrtb_ext.ExtRequestPrebid
  1613  		cacheInstructions extCacheInstructions
  1614  	}
  1615  	type outTest struct {
  1616  		targetData    *targetData
  1617  		nilTargetData bool
  1618  	}
  1619  	testCases := []struct {
  1620  		desc string
  1621  		in   inTest
  1622  		out  outTest
  1623  	}{
  1624  		{
  1625  			"nil requestExt, nil outTargetData",
  1626  			inTest{
  1627  				requestExtPrebid: nil,
  1628  				cacheInstructions: extCacheInstructions{
  1629  					cacheBids: true,
  1630  					cacheVAST: true,
  1631  				},
  1632  			},
  1633  			outTest{targetData: nil, nilTargetData: true},
  1634  		},
  1635  		{
  1636  			"Valid requestExt, nil Targeting field, nil outTargetData",
  1637  			inTest{
  1638  				requestExtPrebid: &openrtb_ext.ExtRequestPrebid{
  1639  					Targeting: nil,
  1640  				},
  1641  				cacheInstructions: extCacheInstructions{
  1642  					cacheBids: true,
  1643  					cacheVAST: true,
  1644  				},
  1645  			},
  1646  			outTest{targetData: nil, nilTargetData: true},
  1647  		},
  1648  		{
  1649  			"Valid targeting data in requestExt, valid outTargetData",
  1650  			inTest{
  1651  				requestExtPrebid: &openrtb_ext.ExtRequestPrebid{
  1652  					Targeting: &openrtb_ext.ExtRequestTargeting{
  1653  						PriceGranularity: &openrtb_ext.PriceGranularity{
  1654  							Precision: ptrutil.ToPtr(2),
  1655  							Ranges:    []openrtb_ext.GranularityRange{{Min: 0.00, Max: 5.00, Increment: 1.00}},
  1656  						},
  1657  						IncludeWinners:    ptrutil.ToPtr(true),
  1658  						IncludeBidderKeys: ptrutil.ToPtr(true),
  1659  					},
  1660  				},
  1661  				cacheInstructions: extCacheInstructions{
  1662  					cacheBids: true,
  1663  					cacheVAST: true,
  1664  				},
  1665  			},
  1666  			outTest{
  1667  				targetData: &targetData{
  1668  					priceGranularity: openrtb_ext.PriceGranularity{
  1669  						Precision: ptrutil.ToPtr(2),
  1670  						Ranges:    []openrtb_ext.GranularityRange{{Min: 0.00, Max: 5.00, Increment: 1.00}},
  1671  					},
  1672  					includeWinners:    true,
  1673  					includeBidderKeys: true,
  1674  					includeCacheBids:  true,
  1675  					includeCacheVast:  true,
  1676  				},
  1677  				nilTargetData: false,
  1678  			},
  1679  		},
  1680  	}
  1681  
  1682  	for _, test := range testCases {
  1683  		actualTargetData := getExtTargetData(test.in.requestExtPrebid, test.in.cacheInstructions)
  1684  
  1685  		if test.out.nilTargetData {
  1686  			assert.Nil(t, actualTargetData, "%s. Targeting data should be nil. \n", test.desc)
  1687  		} else {
  1688  			assert.NotNil(t, actualTargetData, "%s. Targeting data should NOT be nil. \n", test.desc)
  1689  			assert.Equal(t, *test.out.targetData, *actualTargetData, "%s. Unexpected targeting data value. \n", test.desc)
  1690  		}
  1691  	}
  1692  }
  1693  
  1694  func TestParseRequestDebugValues(t *testing.T) {
  1695  	testCases := []struct {
  1696  		desc           string
  1697  		givenTest      int8
  1698  		givenExtPrebid *openrtb_ext.ExtRequestPrebid
  1699  		expected       bool
  1700  	}{
  1701  		{
  1702  			desc:           "bid request test == 0, nil requestExt",
  1703  			givenTest:      0,
  1704  			givenExtPrebid: nil,
  1705  			expected:       false,
  1706  		},
  1707  		{
  1708  			desc:           "bid request test == 0, requestExt debug flag false",
  1709  			givenTest:      0,
  1710  			givenExtPrebid: &openrtb_ext.ExtRequestPrebid{Debug: false},
  1711  			expected:       false,
  1712  		},
  1713  		{
  1714  			desc:           "bid request test == 1, requestExt debug flag false",
  1715  			givenTest:      1,
  1716  			givenExtPrebid: &openrtb_ext.ExtRequestPrebid{Debug: false},
  1717  			expected:       true,
  1718  		},
  1719  		{
  1720  			desc:           "bid request test == 0, requestExt debug flag true",
  1721  			givenTest:      0,
  1722  			givenExtPrebid: &openrtb_ext.ExtRequestPrebid{Debug: true},
  1723  			expected:       true,
  1724  		},
  1725  		{
  1726  			desc:           "bid request test == 1, requestExt debug flag true",
  1727  			givenTest:      1,
  1728  			givenExtPrebid: &openrtb_ext.ExtRequestPrebid{Debug: true},
  1729  			expected:       true,
  1730  		},
  1731  	}
  1732  	for _, test := range testCases {
  1733  		actualDebugInfo := parseRequestDebugValues(test.givenTest, test.givenExtPrebid)
  1734  
  1735  		assert.Equal(t, test.expected, actualDebugInfo, "%s. Unexpected debug value. \n", test.desc)
  1736  	}
  1737  }
  1738  
  1739  func TestSetDebugLogValues(t *testing.T) {
  1740  
  1741  	type aTest struct {
  1742  		desc               string
  1743  		inAccountDebugFlag bool
  1744  		inDebugLog         *DebugLog
  1745  		expectedDebugLog   *DebugLog
  1746  	}
  1747  
  1748  	testGroups := []struct {
  1749  		desc      string
  1750  		testCases []aTest
  1751  	}{
  1752  
  1753  		{
  1754  			"nil debug log",
  1755  			[]aTest{
  1756  				{
  1757  					desc:               "accountDebugFlag false, expect all false flags in resulting debugLog",
  1758  					inAccountDebugFlag: false,
  1759  					inDebugLog:         nil,
  1760  					expectedDebugLog:   &DebugLog{},
  1761  				},
  1762  				{
  1763  					desc:               "accountDebugFlag true, expect debugLog.Enabled to be true",
  1764  					inAccountDebugFlag: true,
  1765  					inDebugLog:         nil,
  1766  					expectedDebugLog:   &DebugLog{Enabled: true},
  1767  				},
  1768  			},
  1769  		},
  1770  		{
  1771  			"non-nil debug log",
  1772  			[]aTest{
  1773  				{
  1774  					desc:               "both accountDebugFlag and DebugEnabledOrOverridden are false, expect debugLog.Enabled to be false",
  1775  					inAccountDebugFlag: false,
  1776  					inDebugLog:         &DebugLog{},
  1777  					expectedDebugLog:   &DebugLog{},
  1778  				},
  1779  				{
  1780  					desc:               "accountDebugFlag false but DebugEnabledOrOverridden is true, expect debugLog.Enabled to be true",
  1781  					inAccountDebugFlag: false,
  1782  					inDebugLog:         &DebugLog{DebugEnabledOrOverridden: true},
  1783  					expectedDebugLog:   &DebugLog{DebugEnabledOrOverridden: true, Enabled: true},
  1784  				},
  1785  				{
  1786  					desc:               "accountDebugFlag true but DebugEnabledOrOverridden is false, expect debugLog.Enabled to be true",
  1787  					inAccountDebugFlag: true,
  1788  					inDebugLog:         &DebugLog{},
  1789  					expectedDebugLog:   &DebugLog{Enabled: true},
  1790  				},
  1791  				{
  1792  					desc:               "Both accountDebugFlag and DebugEnabledOrOverridden are true, expect debugLog.Enabled to be true",
  1793  					inAccountDebugFlag: true,
  1794  					inDebugLog:         &DebugLog{DebugEnabledOrOverridden: true},
  1795  					expectedDebugLog:   &DebugLog{DebugEnabledOrOverridden: true, Enabled: true},
  1796  				},
  1797  			},
  1798  		},
  1799  	}
  1800  
  1801  	for _, group := range testGroups {
  1802  		for _, tc := range group.testCases {
  1803  			// run
  1804  			actualDebugLog := setDebugLogValues(tc.inAccountDebugFlag, tc.inDebugLog)
  1805  			// assertions
  1806  			assert.Equal(t, tc.expectedDebugLog, actualDebugLog, "%s. %s", group.desc, tc.desc)
  1807  		}
  1808  	}
  1809  }
  1810  
  1811  func TestGetExtBidAdjustmentFactors(t *testing.T) {
  1812  	testCases := []struct {
  1813  		desc                    string
  1814  		requestExtPrebid        *openrtb_ext.ExtRequestPrebid
  1815  		outBidAdjustmentFactors map[string]float64
  1816  	}{
  1817  		{
  1818  			desc:                    "Nil request ext",
  1819  			requestExtPrebid:        nil,
  1820  			outBidAdjustmentFactors: nil,
  1821  		},
  1822  		{
  1823  			desc:                    "Non-nil request ext, nil BidAdjustmentFactors field",
  1824  			requestExtPrebid:        &openrtb_ext.ExtRequestPrebid{BidAdjustmentFactors: nil},
  1825  			outBidAdjustmentFactors: nil,
  1826  		},
  1827  		{
  1828  			desc:                    "Non-nil request ext, valid BidAdjustmentFactors field",
  1829  			requestExtPrebid:        &openrtb_ext.ExtRequestPrebid{BidAdjustmentFactors: map[string]float64{"bid-factor": 1.0}},
  1830  			outBidAdjustmentFactors: map[string]float64{"bid-factor": 1.0},
  1831  		},
  1832  		{
  1833  			desc:                    "BidAdjustmentFactors contains uppercase bidders, expect case insensitve map returned",
  1834  			requestExtPrebid:        &openrtb_ext.ExtRequestPrebid{BidAdjustmentFactors: map[string]float64{"Bidder": 1.0, "APPNEXUS": 2.0}},
  1835  			outBidAdjustmentFactors: map[string]float64{"bidder": 1.0, "appnexus": 2.0},
  1836  		},
  1837  	}
  1838  	for _, test := range testCases {
  1839  		actualBidAdjustmentFactors := getExtBidAdjustmentFactors(test.requestExtPrebid)
  1840  
  1841  		assert.Equal(t, test.outBidAdjustmentFactors, actualBidAdjustmentFactors, "%s. Unexpected BidAdjustmentFactors value. \n", test.desc)
  1842  	}
  1843  }
  1844  
  1845  func TestCleanOpenRTBRequestsLMT(t *testing.T) {
  1846  	var (
  1847  		enabled  int8 = 1
  1848  		disabled int8 = 0
  1849  	)
  1850  	testCases := []struct {
  1851  		description         string
  1852  		lmt                 *int8
  1853  		enforceLMT          bool
  1854  		expectDataScrub     bool
  1855  		expectPrivacyLabels metrics.PrivacyLabels
  1856  	}{
  1857  		{
  1858  			description:     "Feature Flag Enabled - OpenTRB Enabled",
  1859  			lmt:             &enabled,
  1860  			enforceLMT:      true,
  1861  			expectDataScrub: true,
  1862  			expectPrivacyLabels: metrics.PrivacyLabels{
  1863  				LMTEnforced: true,
  1864  			},
  1865  		},
  1866  		{
  1867  			description:     "Feature Flag Disabled - OpenTRB Enabled",
  1868  			lmt:             &enabled,
  1869  			enforceLMT:      false,
  1870  			expectDataScrub: false,
  1871  			expectPrivacyLabels: metrics.PrivacyLabels{
  1872  				LMTEnforced: false,
  1873  			},
  1874  		},
  1875  		{
  1876  			description:     "Feature Flag Enabled - OpenTRB Disabled",
  1877  			lmt:             &disabled,
  1878  			enforceLMT:      true,
  1879  			expectDataScrub: false,
  1880  			expectPrivacyLabels: metrics.PrivacyLabels{
  1881  				LMTEnforced: false,
  1882  			},
  1883  		},
  1884  		{
  1885  			description:     "Feature Flag Disabled - OpenTRB Disabled",
  1886  			lmt:             &disabled,
  1887  			enforceLMT:      false,
  1888  			expectDataScrub: false,
  1889  			expectPrivacyLabels: metrics.PrivacyLabels{
  1890  				LMTEnforced: false,
  1891  			},
  1892  		},
  1893  	}
  1894  
  1895  	for _, test := range testCases {
  1896  		req := newBidRequest(t)
  1897  		req.Device.Lmt = test.lmt
  1898  
  1899  		auctionReq := AuctionRequest{
  1900  			BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: req},
  1901  			UserSyncs:         &emptyUsersync{},
  1902  			TCF2Config:        gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
  1903  		}
  1904  
  1905  		gdprPermissionsBuilder := fakePermissionsBuilder{
  1906  			permissions: &permissionsMock{
  1907  				allowAllBidders: true,
  1908  			},
  1909  		}.Builder
  1910  
  1911  		privacyConfig := config.Privacy{
  1912  			LMT: config.LMT{
  1913  				Enforce: test.enforceLMT,
  1914  			},
  1915  		}
  1916  
  1917  		reqSplitter := &requestSplitter{
  1918  			bidderToSyncerKey: map[string]string{},
  1919  			me:                &metrics.MetricsEngineMock{},
  1920  			privacyConfig:     privacyConfig,
  1921  			gdprPermsBuilder:  gdprPermissionsBuilder,
  1922  			hostSChainNode:    nil,
  1923  			bidderInfo:        config.BidderInfos{},
  1924  		}
  1925  
  1926  		results, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, false, map[string]float64{})
  1927  		result := results[0]
  1928  
  1929  		assert.Nil(t, errs)
  1930  		if test.expectDataScrub {
  1931  			assert.Equal(t, result.BidRequest.User.BuyerUID, "", test.description+":User.BuyerUID")
  1932  			assert.Equal(t, result.BidRequest.Device.DIDMD5, "", test.description+":Device.DIDMD5")
  1933  		} else {
  1934  			assert.NotEqual(t, result.BidRequest.User.BuyerUID, "", test.description+":User.BuyerUID")
  1935  			assert.NotEqual(t, result.BidRequest.Device.DIDMD5, "", test.description+":Device.DIDMD5")
  1936  		}
  1937  		assert.Equal(t, test.expectPrivacyLabels, privacyLabels, test.description+":PrivacyLabels")
  1938  	}
  1939  }
  1940  
  1941  func TestCleanOpenRTBRequestsGDPR(t *testing.T) {
  1942  	tcf2Consent := "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA"
  1943  
  1944  	testCases := []struct {
  1945  		description         string
  1946  		gdprConsent         string
  1947  		gdprScrub           bool
  1948  		gdprSignal          gdpr.Signal
  1949  		gdprEnforced        bool
  1950  		permissionsError    error
  1951  		expectPrivacyLabels metrics.PrivacyLabels
  1952  		expectError         bool
  1953  	}{
  1954  		{
  1955  			description:  "enforce no scrub - TCF invalid",
  1956  			gdprConsent:  "malformed",
  1957  			gdprScrub:    false,
  1958  			gdprSignal:   gdpr.SignalYes,
  1959  			gdprEnforced: true,
  1960  			expectPrivacyLabels: metrics.PrivacyLabels{
  1961  				GDPREnforced:   true,
  1962  				GDPRTCFVersion: "",
  1963  			},
  1964  		},
  1965  		{
  1966  			description:  "enforce and scrub",
  1967  			gdprConsent:  tcf2Consent,
  1968  			gdprScrub:    true,
  1969  			gdprSignal:   gdpr.SignalYes,
  1970  			gdprEnforced: true,
  1971  			expectPrivacyLabels: metrics.PrivacyLabels{
  1972  				GDPREnforced:   true,
  1973  				GDPRTCFVersion: metrics.TCFVersionV2,
  1974  			},
  1975  		},
  1976  		{
  1977  			description:  "not enforce",
  1978  			gdprConsent:  tcf2Consent,
  1979  			gdprScrub:    false,
  1980  			gdprSignal:   gdpr.SignalYes,
  1981  			gdprEnforced: false,
  1982  			expectPrivacyLabels: metrics.PrivacyLabels{
  1983  				GDPREnforced:   false,
  1984  				GDPRTCFVersion: "",
  1985  			},
  1986  		},
  1987  		{
  1988  			description:      "enforce - error while checking if personal info is allowed",
  1989  			gdprConsent:      tcf2Consent,
  1990  			gdprScrub:        true,
  1991  			permissionsError: errors.New("Some error"),
  1992  			gdprSignal:       gdpr.SignalYes,
  1993  			gdprEnforced:     true,
  1994  			expectPrivacyLabels: metrics.PrivacyLabels{
  1995  				GDPREnforced:   true,
  1996  				GDPRTCFVersion: metrics.TCFVersionV2,
  1997  			},
  1998  		},
  1999  	}
  2000  
  2001  	for _, test := range testCases {
  2002  		req := newBidRequest(t)
  2003  		req.User.Ext = json.RawMessage(`{"consent":"` + test.gdprConsent + `"}`)
  2004  
  2005  		privacyConfig := config.Privacy{}
  2006  		accountConfig := config.Account{}
  2007  
  2008  		auctionReq := AuctionRequest{
  2009  			BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: req},
  2010  			UserSyncs:         &emptyUsersync{},
  2011  			Account:           accountConfig,
  2012  			TCF2Config: gdpr.NewTCF2Config(
  2013  				privacyConfig.GDPR.TCF2,
  2014  				accountConfig.GDPR,
  2015  			),
  2016  		}
  2017  
  2018  		gdprPermissionsBuilder := fakePermissionsBuilder{
  2019  			permissions: &permissionsMock{
  2020  				allowAllBidders: true,
  2021  				passGeo:         !test.gdprScrub,
  2022  				passID:          !test.gdprScrub,
  2023  				activitiesError: test.permissionsError,
  2024  			},
  2025  		}.Builder
  2026  
  2027  		metricsMock := metrics.MetricsEngineMock{}
  2028  		metricsMock.Mock.On("RecordAdapterBuyerUIDScrubbed", mock.Anything).Return()
  2029  
  2030  		reqSplitter := &requestSplitter{
  2031  			bidderToSyncerKey: map[string]string{},
  2032  			me:                &metricsMock,
  2033  			privacyConfig:     privacyConfig,
  2034  			gdprPermsBuilder:  gdprPermissionsBuilder,
  2035  			hostSChainNode:    nil,
  2036  			bidderInfo:        config.BidderInfos{},
  2037  		}
  2038  
  2039  		results, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, test.gdprSignal, test.gdprEnforced, map[string]float64{})
  2040  		result := results[0]
  2041  
  2042  		if test.expectError {
  2043  			assert.NotNil(t, errs)
  2044  		} else {
  2045  			assert.Nil(t, errs)
  2046  		}
  2047  
  2048  		if test.gdprScrub {
  2049  			assert.Equal(t, result.BidRequest.User.BuyerUID, "", test.description+":User.BuyerUID")
  2050  			assert.Equal(t, result.BidRequest.Device.DIDMD5, "", test.description+":Device.DIDMD5")
  2051  			metricsMock.AssertCalled(t, "RecordAdapterBuyerUIDScrubbed", openrtb_ext.BidderAppnexus)
  2052  		} else {
  2053  			assert.NotEqual(t, result.BidRequest.User.BuyerUID, "", test.description+":User.BuyerUID")
  2054  			assert.NotEqual(t, result.BidRequest.Device.DIDMD5, "", test.description+":Device.DIDMD5")
  2055  			metricsMock.AssertNotCalled(t, "RecordAdapterBuyerUIDScrubbed", openrtb_ext.BidderAppnexus)
  2056  		}
  2057  		assert.Equal(t, test.expectPrivacyLabels, privacyLabels, test.description+":PrivacyLabels")
  2058  	}
  2059  }
  2060  
  2061  func TestCleanOpenRTBRequestsGDPRBlockBidRequest(t *testing.T) {
  2062  	testCases := []struct {
  2063  		description            string
  2064  		gdprEnforced           bool
  2065  		gdprAllowedBidders     []openrtb_ext.BidderName
  2066  		expectedBidders        []openrtb_ext.BidderName
  2067  		expectedBlockedBidders []openrtb_ext.BidderName
  2068  	}{
  2069  		{
  2070  			description:            "gdpr enforced, one request allowed and one request blocked",
  2071  			gdprEnforced:           true,
  2072  			gdprAllowedBidders:     []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus},
  2073  			expectedBidders:        []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus},
  2074  			expectedBlockedBidders: []openrtb_ext.BidderName{openrtb_ext.BidderRubicon},
  2075  		},
  2076  		{
  2077  			description:            "gdpr enforced, two requests allowed and no requests blocked",
  2078  			gdprEnforced:           true,
  2079  			gdprAllowedBidders:     []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus, openrtb_ext.BidderRubicon},
  2080  			expectedBidders:        []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus, openrtb_ext.BidderRubicon},
  2081  			expectedBlockedBidders: []openrtb_ext.BidderName{},
  2082  		},
  2083  		{
  2084  			description:            "gdpr not enforced, two requests allowed and no requests blocked",
  2085  			gdprEnforced:           false,
  2086  			gdprAllowedBidders:     []openrtb_ext.BidderName{},
  2087  			expectedBidders:        []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus, openrtb_ext.BidderRubicon},
  2088  			expectedBlockedBidders: []openrtb_ext.BidderName{},
  2089  		},
  2090  	}
  2091  
  2092  	for _, test := range testCases {
  2093  		req := newBidRequest(t)
  2094  		req.Regs = &openrtb2.Regs{
  2095  			Ext: json.RawMessage(`{"gdpr":1}`),
  2096  		}
  2097  		req.Imp[0].Ext = json.RawMessage(`{"prebid":{"bidder":{"appnexus": {"placementId": 1}, "rubicon": {}}}}`)
  2098  
  2099  		privacyConfig := config.Privacy{}
  2100  		accountConfig := config.Account{
  2101  			GDPR: config.AccountGDPR{
  2102  				Enabled: nil,
  2103  			},
  2104  		}
  2105  
  2106  		auctionReq := AuctionRequest{
  2107  			BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: req},
  2108  			UserSyncs:         &emptyUsersync{},
  2109  			Account:           accountConfig,
  2110  			TCF2Config:        gdpr.NewTCF2Config(privacyConfig.GDPR.TCF2, accountConfig.GDPR),
  2111  		}
  2112  
  2113  		gdprPermissionsBuilder := fakePermissionsBuilder{
  2114  			permissions: &permissionsMock{
  2115  				allowedBidders:  test.gdprAllowedBidders,
  2116  				passGeo:         true,
  2117  				passID:          true,
  2118  				activitiesError: nil,
  2119  			},
  2120  		}.Builder
  2121  
  2122  		metricsMock := metrics.MetricsEngineMock{}
  2123  		metricsMock.Mock.On("RecordAdapterGDPRRequestBlocked", mock.Anything).Return()
  2124  
  2125  		reqSplitter := &requestSplitter{
  2126  			bidderToSyncerKey: map[string]string{},
  2127  			me:                &metricsMock,
  2128  			privacyConfig:     privacyConfig,
  2129  			gdprPermsBuilder:  gdprPermissionsBuilder,
  2130  			hostSChainNode:    nil,
  2131  			bidderInfo:        config.BidderInfos{},
  2132  		}
  2133  
  2134  		results, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalYes, test.gdprEnforced, map[string]float64{})
  2135  
  2136  		// extract bidder name from each request in the results
  2137  		bidders := []openrtb_ext.BidderName{}
  2138  		for _, req := range results {
  2139  			bidders = append(bidders, req.BidderName)
  2140  		}
  2141  
  2142  		assert.Empty(t, errs, test.description)
  2143  		assert.ElementsMatch(t, bidders, test.expectedBidders, test.description)
  2144  
  2145  		for _, blockedBidder := range test.expectedBlockedBidders {
  2146  			metricsMock.AssertCalled(t, "RecordAdapterGDPRRequestBlocked", blockedBidder)
  2147  		}
  2148  		for _, allowedBidder := range test.expectedBidders {
  2149  			metricsMock.AssertNotCalled(t, "RecordAdapterGDPRRequestBlocked", allowedBidder)
  2150  		}
  2151  	}
  2152  }
  2153  
  2154  func TestCleanOpenRTBRequestsWithOpenRTBDowngrade(t *testing.T) {
  2155  	emptyTCF2Config := gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{})
  2156  
  2157  	bidReq := newBidRequest(t)
  2158  	bidReq.Regs = &openrtb2.Regs{}
  2159  	bidReq.Regs.GPP = "DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1NYN"
  2160  	bidReq.Regs.GPPSID = []int8{6}
  2161  	bidReq.User.ID = ""
  2162  	bidReq.User.BuyerUID = ""
  2163  	bidReq.User.Yob = 0
  2164  	bidReq.User.Gender = ""
  2165  	bidReq.User.Geo = &openrtb2.Geo{Lat: ptrutil.ToPtr(123.46)}
  2166  
  2167  	downgradedRegs := *bidReq.Regs
  2168  	downgradedUser := *bidReq.User
  2169  	downgradedRegs.GDPR = ptrutil.ToPtr[int8](0)
  2170  	downgradedRegs.USPrivacy = "1NYN"
  2171  	downgradedUser.Consent = "CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA"
  2172  
  2173  	testCases := []struct {
  2174  		name        string
  2175  		req         AuctionRequest
  2176  		expectRegs  *openrtb2.Regs
  2177  		expectUser  *openrtb2.User
  2178  		bidderInfos config.BidderInfos
  2179  	}{
  2180  		{
  2181  			name:        "NotSupported",
  2182  			req:         AuctionRequest{BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: bidReq}, UserSyncs: &emptyUsersync{}, TCF2Config: emptyTCF2Config},
  2183  			expectRegs:  &downgradedRegs,
  2184  			expectUser:  &downgradedUser,
  2185  			bidderInfos: config.BidderInfos{"appnexus": config.BidderInfo{OpenRTB: &config.OpenRTBInfo{GPPSupported: false}}},
  2186  		},
  2187  		{
  2188  			name:        "Supported",
  2189  			req:         AuctionRequest{BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: bidReq}, UserSyncs: &emptyUsersync{}, TCF2Config: emptyTCF2Config},
  2190  			expectRegs:  bidReq.Regs,
  2191  			expectUser:  bidReq.User,
  2192  			bidderInfos: config.BidderInfos{"appnexus": config.BidderInfo{OpenRTB: &config.OpenRTBInfo{GPPSupported: true}}},
  2193  		},
  2194  	}
  2195  
  2196  	privacyConfig := config.Privacy{
  2197  		CCPA: config.CCPA{
  2198  			Enforce: true,
  2199  		},
  2200  		LMT: config.LMT{
  2201  			Enforce: true,
  2202  		},
  2203  	}
  2204  
  2205  	for _, test := range testCases {
  2206  		t.Run(test.name, func(t *testing.T) {
  2207  
  2208  			gdprPermsBuilder := fakePermissionsBuilder{
  2209  				permissions: &permissionsMock{
  2210  					allowAllBidders: true,
  2211  				},
  2212  			}.Builder
  2213  
  2214  			reqSplitter := &requestSplitter{
  2215  				bidderToSyncerKey: map[string]string{},
  2216  				me:                &metrics.MetricsEngineMock{},
  2217  				privacyConfig:     privacyConfig,
  2218  				gdprPermsBuilder:  gdprPermsBuilder,
  2219  				hostSChainNode:    nil,
  2220  				bidderInfo:        test.bidderInfos,
  2221  			}
  2222  			bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo, false, map[string]float64{})
  2223  			assert.Nil(t, err, "Err should be nil")
  2224  			bidRequest := bidderRequests[0]
  2225  			assert.Equal(t, test.expectRegs, bidRequest.BidRequest.Regs)
  2226  			assert.Equal(t, test.expectUser, bidRequest.BidRequest.User)
  2227  
  2228  		})
  2229  	}
  2230  }
  2231  
  2232  func TestBuildRequestExtForBidder(t *testing.T) {
  2233  	var (
  2234  		bidder       = "foo"
  2235  		bidderParams = json.RawMessage(`"bar"`)
  2236  	)
  2237  
  2238  	testCases := []struct {
  2239  		description          string
  2240  		requestExt           json.RawMessage
  2241  		bidderParams         map[string]json.RawMessage
  2242  		alternateBidderCodes *openrtb_ext.ExtAlternateBidderCodes
  2243  		expectedJson         json.RawMessage
  2244  	}{
  2245  		{
  2246  			description:          "Nil",
  2247  			bidderParams:         nil,
  2248  			requestExt:           nil,
  2249  			alternateBidderCodes: nil,
  2250  			expectedJson:         nil,
  2251  		},
  2252  		{
  2253  			description:          "Empty",
  2254  			bidderParams:         nil,
  2255  			alternateBidderCodes: nil,
  2256  			requestExt:           json.RawMessage(`{}`),
  2257  			expectedJson:         nil,
  2258  		},
  2259  		{
  2260  			description:  "Prebid - Allowed Fields Only",
  2261  			bidderParams: nil,
  2262  			requestExt:   json.RawMessage(`{"prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true}, "server": {"externalurl": "url", "gvlid": 1, "datacenter": "2"}, "sdk": {"renderers": [{"name": "r1"}]}}}`),
  2263  			expectedJson: json.RawMessage(`{"prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true}, "server": {"externalurl": "url", "gvlid": 1, "datacenter": "2"}, "sdk": {"renderers": [{"name": "r1"}]}}}`),
  2264  		},
  2265  		{
  2266  			description:  "Prebid - Allowed Fields + Bidder Params",
  2267  			bidderParams: map[string]json.RawMessage{bidder: bidderParams},
  2268  			requestExt:   json.RawMessage(`{"prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true}, "server": {"externalurl": "url", "gvlid": 1, "datacenter": "2"}, "sdk": {"renderers": [{"name": "r1"}]}}}`),
  2269  			expectedJson: json.RawMessage(`{"prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true}, "server": {"externalurl": "url", "gvlid": 1, "datacenter": "2"}, "sdk": {"renderers": [{"name": "r1"}]}, "bidderparams":"bar"}}`),
  2270  		},
  2271  		{
  2272  			description:  "Other",
  2273  			bidderParams: nil,
  2274  			requestExt:   json.RawMessage(`{"other":"foo"}`),
  2275  			expectedJson: json.RawMessage(`{"other":"foo"}`),
  2276  		},
  2277  		{
  2278  			description:  "Prebid + Other + Bider Params",
  2279  			bidderParams: map[string]json.RawMessage{bidder: bidderParams},
  2280  			requestExt:   json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true}, "server": {"externalurl": "url", "gvlid": 1, "datacenter": "2"}, "sdk": {"renderers": [{"name": "r1"}]}}}`),
  2281  			expectedJson: json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true}, "server": {"externalurl": "url", "gvlid": 1, "datacenter": "2"}, "sdk": {"renderers": [{"name": "r1"}]}, "bidderparams":"bar"}}`),
  2282  		},
  2283  		{
  2284  			description:          "Prebid + AlternateBidderCodes in pbs config but current bidder not in AlternateBidderCodes config",
  2285  			bidderParams:         map[string]json.RawMessage{bidder: bidderParams},
  2286  			alternateBidderCodes: &openrtb_ext.ExtAlternateBidderCodes{Enabled: true, Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{"bar": {Enabled: true, AllowedBidderCodes: []string{"*"}}}},
  2287  			requestExt:           json.RawMessage(`{"other":"foo"}`),
  2288  			expectedJson:         json.RawMessage(`{"other":"foo","prebid":{"alternatebiddercodes":{"enabled":true,"bidders":null},"bidderparams":"bar"}}`),
  2289  		},
  2290  		{
  2291  			description:          "Prebid + AlternateBidderCodes in request",
  2292  			bidderParams:         map[string]json.RawMessage{bidder: bidderParams},
  2293  			alternateBidderCodes: &openrtb_ext.ExtAlternateBidderCodes{},
  2294  			requestExt:           json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"alternatebiddercodes":{"enabled":true,"bidders":{"foo":{"enabled":true,"allowedbiddercodes":["foo2"]},"bar":{"enabled":true,"allowedbiddercodes":["ix"]}}}}}`),
  2295  			expectedJson:         json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"alternatebiddercodes":{"enabled":true,"bidders":{"foo":{"enabled":true,"allowedbiddercodes":["foo2"]}}},"bidderparams":"bar"}}`),
  2296  		},
  2297  		{
  2298  			description:          "Prebid + AlternateBidderCodes in request but current bidder not in AlternateBidderCodes config",
  2299  			bidderParams:         map[string]json.RawMessage{bidder: bidderParams},
  2300  			alternateBidderCodes: &openrtb_ext.ExtAlternateBidderCodes{},
  2301  			requestExt:           json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"alternatebiddercodes":{"enabled":true,"bidders":{"bar":{"enabled":true,"allowedbiddercodes":["ix"]}}}}}`),
  2302  			expectedJson:         json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"alternatebiddercodes":{"enabled":true,"bidders":null},"bidderparams":"bar"}}`),
  2303  		},
  2304  		{
  2305  			description:          "Prebid + AlternateBidderCodes in both pbs config and in the request",
  2306  			bidderParams:         map[string]json.RawMessage{bidder: bidderParams},
  2307  			alternateBidderCodes: &openrtb_ext.ExtAlternateBidderCodes{Enabled: true, Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{"foo": {Enabled: true, AllowedBidderCodes: []string{"*"}}}},
  2308  			requestExt:           json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"alternatebiddercodes":{"enabled":true,"bidders":{"foo":{"enabled":true,"allowedbiddercodes":["foo2"]},"bar":{"enabled":true,"allowedbiddercodes":["ix"]}}}}}`),
  2309  			expectedJson:         json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"alternatebiddercodes":{"enabled":true,"bidders":{"foo":{"enabled":true,"allowedbiddercodes":["foo2"]}}},"bidderparams":"bar"}}`),
  2310  		},
  2311  		{
  2312  			description:  "Prebid + Other + Bider Params + MultiBid.Bidder",
  2313  			bidderParams: map[string]json.RawMessage{bidder: bidderParams},
  2314  			requestExt:   json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"multibid":[{"bidder":"foo","maxbids":2,"targetbiddercodeprefix":"fmb"},{"bidders":["appnexus","groupm"],"maxbids":2}]}}`),
  2315  			expectedJson: json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"multibid":[{"bidder":"foo","maxbids":2,"targetbiddercodeprefix":"fmb"}],"bidderparams":"bar"}}`),
  2316  		},
  2317  		{
  2318  			description:  "Prebid + Other + Bider Params + MultiBid.Bidders",
  2319  			bidderParams: map[string]json.RawMessage{bidder: bidderParams},
  2320  			requestExt:   json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"multibid":[{"bidder":"pubmatic","maxbids":3,"targetbiddercodeprefix":"pubM"},{"bidders":["foo","groupm"],"maxbids":4}]}}`),
  2321  			expectedJson: json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"multibid":[{"bidders":["foo"],"maxbids":4}],"bidderparams":"bar"}}`),
  2322  		},
  2323  		{
  2324  			description:  "Prebid + Other + Bider Params + MultiBid (foo not in MultiBid)",
  2325  			bidderParams: map[string]json.RawMessage{bidder: bidderParams},
  2326  			requestExt:   json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"multibid":[{"bidder":"foo2","maxbids":2,"targetbiddercodeprefix":"fmb"},{"bidders":["appnexus","groupm"],"maxbids":2}]}}`),
  2327  			expectedJson: json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"bidderparams":"bar"}}`),
  2328  		},
  2329  		{
  2330  			description:  "Prebid + Other + Bider Params + MultiBid (foo not in MultiBid)",
  2331  			bidderParams: map[string]json.RawMessage{bidder: bidderParams},
  2332  			requestExt:   json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"multibid":[{"bidder":"foo2","maxbids":2,"targetbiddercodeprefix":"fmb"},{"bidders":["appnexus","groupm"],"maxbids":2}]}}`),
  2333  			expectedJson: json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"bidderparams":"bar"}}`),
  2334  		},
  2335  		{
  2336  			description:  "Prebid + AlternateBidderCodes.MultiBid.Bidder",
  2337  			requestExt:   json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"alternatebiddercodes":{"enabled":true,"bidders":{"foo":{"enabled":true,"allowedbiddercodes":["pubmatic"]}}},"multibid":[{"bidder":"foo","maxbids":3,"targetbiddercodeprefix":"fmb"},{"bidder":"foo2","maxbids":4,"targetbiddercodeprefix":"fmb2"},{"bidder":"pubmatic","maxbids":5,"targetbiddercodeprefix":"pm"}]}}`),
  2338  			expectedJson: json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"alternatebiddercodes":{"enabled":true,"bidders":{"foo":{"enabled":true,"allowedbiddercodes":["pubmatic"]}}},"multibid":[{"bidder":"foo","maxbids":3,"targetbiddercodeprefix":"fmb"},{"bidder":"pubmatic","maxbids":5,"targetbiddercodeprefix":"pm"}]}}`),
  2339  		},
  2340  		{
  2341  			description:  "Prebid + AlternateBidderCodes.MultiBid.Bidders",
  2342  			requestExt:   json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"alternatebiddercodes":{"enabled":true,"bidders":{"foo":{"enabled":true,"allowedbiddercodes":["pubmatic"]}}},"multibid":[{"bidder":"foo","maxbids":3,"targetbiddercodeprefix":"fmb"},{"bidders":["pubmatic","groupm"],"maxbids":4}]}}`),
  2343  			expectedJson: json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"alternatebiddercodes":{"enabled":true,"bidders":{"foo":{"enabled":true,"allowedbiddercodes":["pubmatic"]}}},"multibid":[{"bidder":"foo","maxbids":3,"targetbiddercodeprefix":"fmb"},{"bidders":["pubmatic"],"maxbids":4}]}}`),
  2344  		},
  2345  		{
  2346  			description:  "Prebid + AlternateBidderCodes.MultiBid.Bidder with *",
  2347  			requestExt:   json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"alternatebiddercodes":{"enabled":true,"bidders":{"foo":{"enabled":true,"allowedbiddercodes":["*"]}}},"multibid":[{"bidder":"foo","maxbids":3,"targetbiddercodeprefix":"fmb"},{"bidder":"foo2","maxbids":4,"targetbiddercodeprefix":"fmb2"},{"bidder":"pubmatic","maxbids":5,"targetbiddercodeprefix":"pm"}]}}`),
  2348  			expectedJson: json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"alternatebiddercodes":{"enabled":true,"bidders":{"foo":{"enabled":true,"allowedbiddercodes":["*"]}}},"multibid":[{"bidder":"foo","maxbids":3,"targetbiddercodeprefix":"fmb"},{"bidder":"foo2","maxbids":4,"targetbiddercodeprefix":"fmb2"},{"bidder":"pubmatic","maxbids":5,"targetbiddercodeprefix":"pm"}]}}`),
  2349  		},
  2350  		{
  2351  			description:  "Prebid + AlternateBidderCodes.MultiBid.Bidders with *",
  2352  			requestExt:   json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"alternatebiddercodes":{"enabled":true,"bidders":{"foo":{"enabled":true,"allowedbiddercodes":["*"]}}},"multibid":[{"bidder":"foo","maxbids":3,"targetbiddercodeprefix":"fmb"},{"bidders":["pubmatic","groupm"],"maxbids":4}]}}`),
  2353  			expectedJson: json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"alternatebiddercodes":{"enabled":true,"bidders":{"foo":{"enabled":true,"allowedbiddercodes":["*"]}}},"multibid":[{"bidder":"foo","maxbids":3,"targetbiddercodeprefix":"fmb"},{"bidders":["pubmatic"],"maxbids":4},{"bidders":["groupm"],"maxbids":4}]}}`),
  2354  		},
  2355  		{
  2356  			description:  "Prebid + AlternateBidderCodes + MultiBid",
  2357  			requestExt:   json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"alternatebiddercodes":{"enabled":true,"bidders":{"foo":{"enabled":true,"allowedbiddercodes":["foo2"]}}},"multibid":[{"bidder":"foo3","maxbids":3,"targetbiddercodeprefix":"fmb"},{"bidders":["pubmatic","groupm"],"maxbids":4}]}}`),
  2358  			expectedJson: json.RawMessage(`{"other":"foo","prebid":{"integration":"a","channel":{"name":"b","version":"c"},"debug":true,"currency":{"rates":{"FOO":{"BAR":42}},"usepbsrates":true},"alternatebiddercodes":{"enabled":true,"bidders":{"foo":{"enabled":true,"allowedbiddercodes":["foo2"]}}}}}`),
  2359  		},
  2360  	}
  2361  
  2362  	for _, test := range testCases {
  2363  		requestExtParsed := &openrtb_ext.ExtRequest{}
  2364  		if test.requestExt != nil {
  2365  			err := jsonutil.UnmarshalValid(test.requestExt, requestExtParsed)
  2366  			if !assert.NoError(t, err, test.description+":parse_ext") {
  2367  				continue
  2368  			}
  2369  		}
  2370  
  2371  		actualJson, actualErr := buildRequestExtForBidder(bidder, test.requestExt, requestExtParsed, test.bidderParams, test.alternateBidderCodes)
  2372  		if len(test.expectedJson) > 0 {
  2373  			assert.JSONEq(t, string(test.expectedJson), string(actualJson), test.description+":json")
  2374  		} else {
  2375  			assert.Equal(t, test.expectedJson, actualJson, test.description+":json")
  2376  		}
  2377  		assert.NoError(t, actualErr, test.description+":err")
  2378  	}
  2379  }
  2380  
  2381  func TestBuildRequestExtForBidder_RequestExtParsedNil(t *testing.T) {
  2382  	var (
  2383  		bidder               = "foo"
  2384  		requestExt           = json.RawMessage(`{}`)
  2385  		requestExtParsed     *openrtb_ext.ExtRequest
  2386  		bidderParams         map[string]json.RawMessage
  2387  		alternateBidderCodes *openrtb_ext.ExtAlternateBidderCodes
  2388  	)
  2389  
  2390  	actualJson, actualErr := buildRequestExtForBidder(bidder, requestExt, requestExtParsed, bidderParams, alternateBidderCodes)
  2391  	assert.Nil(t, actualJson)
  2392  	assert.NoError(t, actualErr)
  2393  }
  2394  
  2395  func TestBuildRequestExtForBidder_RequestExtMalformed(t *testing.T) {
  2396  	var (
  2397  		bidder               = "foo"
  2398  		requestExt           = json.RawMessage(`malformed`)
  2399  		requestExtParsed     = &openrtb_ext.ExtRequest{}
  2400  		bidderParams         map[string]json.RawMessage
  2401  		alternateBidderCodes *openrtb_ext.ExtAlternateBidderCodes
  2402  	)
  2403  
  2404  	actualJson, actualErr := buildRequestExtForBidder(bidder, requestExt, requestExtParsed, bidderParams, alternateBidderCodes)
  2405  	assert.Equal(t, json.RawMessage(nil), actualJson)
  2406  	assert.EqualError(t, actualErr, "expect { or n, but found m")
  2407  }
  2408  
  2409  // newAdapterAliasBidRequest builds a BidRequest with aliases
  2410  func newAdapterAliasBidRequest(t *testing.T) *openrtb2.BidRequest {
  2411  	dnt := int8(1)
  2412  	return &openrtb2.BidRequest{
  2413  		Site: &openrtb2.Site{
  2414  			Page:   "www.some.domain.com",
  2415  			Domain: "domain.com",
  2416  			Publisher: &openrtb2.Publisher{
  2417  				ID: "some-publisher-id",
  2418  			},
  2419  		},
  2420  		Device: &openrtb2.Device{
  2421  			DIDMD5:   "some device ID hash",
  2422  			UA:       deviceUA,
  2423  			IFA:      "ifa",
  2424  			IP:       "132.173.230.74",
  2425  			DNT:      &dnt,
  2426  			Language: "EN",
  2427  		},
  2428  		Source: &openrtb2.Source{
  2429  			TID: "testTID",
  2430  		},
  2431  		User: &openrtb2.User{
  2432  			ID:       "our-id",
  2433  			BuyerUID: "their-id",
  2434  			Ext:      json.RawMessage(`{"consent":"BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw"}`),
  2435  		},
  2436  		Regs: &openrtb2.Regs{
  2437  			Ext: json.RawMessage(`{"gdpr":1}`),
  2438  		},
  2439  		Imp: []openrtb2.Imp{{
  2440  			ID: "some-imp-id",
  2441  			Banner: &openrtb2.Banner{
  2442  				Format: []openrtb2.Format{{
  2443  					W: 300,
  2444  					H: 250,
  2445  				}, {
  2446  					W: 300,
  2447  					H: 600,
  2448  				}},
  2449  			},
  2450  			Ext: json.RawMessage(`{"appnexus": {"placementId": 1},"somealias": {"placementId": 105}}`),
  2451  		}},
  2452  		Ext: json.RawMessage(`{"prebid":{"aliases":{"somealias":"appnexus"}}}`),
  2453  	}
  2454  }
  2455  
  2456  func newBidRequest(t *testing.T) *openrtb2.BidRequest {
  2457  	return &openrtb2.BidRequest{
  2458  		Site: &openrtb2.Site{
  2459  			Page:   "www.some.domain.com",
  2460  			Domain: "domain.com",
  2461  			Publisher: &openrtb2.Publisher{
  2462  				ID: "some-publisher-id",
  2463  			},
  2464  		},
  2465  		Device: &openrtb2.Device{
  2466  			UA:       deviceUA,
  2467  			IP:       "132.173.230.74",
  2468  			Language: "EN",
  2469  			DIDMD5:   "DIDMD5",
  2470  			IFA:      "IFA",
  2471  			DIDSHA1:  "DIDSHA1",
  2472  			DPIDMD5:  "DPIDMD5",
  2473  			DPIDSHA1: "DPIDSHA1",
  2474  			MACMD5:   "MACMD5",
  2475  			MACSHA1:  "MACSHA1",
  2476  			Geo:      &openrtb2.Geo{Lat: ptrutil.ToPtr(123.456), Lon: ptrutil.ToPtr(11.278)},
  2477  		},
  2478  		Source: &openrtb2.Source{
  2479  			TID: "testTID",
  2480  		},
  2481  		User: &openrtb2.User{
  2482  			ID:       "our-id",
  2483  			BuyerUID: "their-id",
  2484  			Yob:      1982,
  2485  			Gender:   "test",
  2486  			Ext:      json.RawMessage(`{"data": 1, "test": 2}`),
  2487  			Geo:      &openrtb2.Geo{Lat: ptrutil.ToPtr(123.456), Lon: ptrutil.ToPtr(11.278)},
  2488  			EIDs: []openrtb2.EID{
  2489  				{Source: "eids-source"},
  2490  			},
  2491  			Data: []openrtb2.Data{{ID: "data-id"}},
  2492  		},
  2493  		Imp: []openrtb2.Imp{{
  2494  			BidFloor: 100,
  2495  			ID:       "some-imp-id",
  2496  			Banner: &openrtb2.Banner{
  2497  				Format: []openrtb2.Format{{
  2498  					W: 300,
  2499  					H: 250,
  2500  				}, {
  2501  					W: 300,
  2502  					H: 600,
  2503  				}},
  2504  			},
  2505  			Ext: json.RawMessage(`{"prebid":{"tid":"1234567", "bidder":{"appnexus": {"placementId": 1}}}}`),
  2506  		}},
  2507  	}
  2508  }
  2509  
  2510  func newBidRequestWithBidderParams(t *testing.T) *openrtb2.BidRequest {
  2511  	return &openrtb2.BidRequest{
  2512  		Site: &openrtb2.Site{
  2513  			Page:   "www.some.domain.com",
  2514  			Domain: "domain.com",
  2515  			Publisher: &openrtb2.Publisher{
  2516  				ID: "some-publisher-id",
  2517  			},
  2518  		},
  2519  		Device: &openrtb2.Device{
  2520  			DIDMD5:   "some device ID hash",
  2521  			UA:       deviceUA,
  2522  			IFA:      "ifa",
  2523  			IP:       "132.173.230.74",
  2524  			Language: "EN",
  2525  		},
  2526  		Source: &openrtb2.Source{
  2527  			TID: "testTID",
  2528  		},
  2529  		User: &openrtb2.User{
  2530  			ID:       "our-id",
  2531  			BuyerUID: "their-id",
  2532  			Yob:      1982,
  2533  			Ext:      json.RawMessage(`{}`),
  2534  		},
  2535  		Imp: []openrtb2.Imp{{
  2536  			ID: "some-imp-id",
  2537  			Banner: &openrtb2.Banner{
  2538  				Format: []openrtb2.Format{{
  2539  					W: 300,
  2540  					H: 250,
  2541  				}, {
  2542  					W: 300,
  2543  					H: 600,
  2544  				}},
  2545  			},
  2546  			Ext: json.RawMessage(`{"prebid":{"bidder":{"appnexus": {"placementId": 1}, "pubmatic":{"publisherId": "1234"}}}}`),
  2547  		}},
  2548  	}
  2549  }
  2550  
  2551  func TestRandomizeList(t *testing.T) {
  2552  	var (
  2553  		bidder1 = openrtb_ext.BidderName("bidder1")
  2554  		bidder2 = openrtb_ext.BidderName("bidder2")
  2555  		bidder3 = openrtb_ext.BidderName("bidder3")
  2556  	)
  2557  
  2558  	testCases := []struct {
  2559  		description string
  2560  		bidders     []openrtb_ext.BidderName
  2561  	}{
  2562  		{
  2563  			description: "None",
  2564  			bidders:     []openrtb_ext.BidderName{},
  2565  		},
  2566  		{
  2567  			description: "One",
  2568  			bidders:     []openrtb_ext.BidderName{bidder1},
  2569  		},
  2570  		{
  2571  			description: "Many",
  2572  			bidders:     []openrtb_ext.BidderName{bidder1, bidder2, bidder3},
  2573  		},
  2574  	}
  2575  
  2576  	for _, test := range testCases {
  2577  		biddersWorkingCopy := make([]openrtb_ext.BidderName, len(test.bidders))
  2578  		copy(biddersWorkingCopy, test.bidders)
  2579  
  2580  		randomizeList(biddersWorkingCopy)
  2581  
  2582  		// test all bidders are still present, ignoring order. we are testing the algorithm doesn't loose
  2583  		// elements. we are not testing the random number generator itself.
  2584  		assert.ElementsMatch(t, test.bidders, biddersWorkingCopy)
  2585  	}
  2586  }
  2587  
  2588  func TestRemoveUnpermissionedEids(t *testing.T) {
  2589  	bidder := "bidderA"
  2590  
  2591  	testCases := []struct {
  2592  		description     string
  2593  		userExt         json.RawMessage
  2594  		eidPermissions  []openrtb_ext.ExtRequestPrebidDataEidPermission
  2595  		expectedUserExt json.RawMessage
  2596  	}{
  2597  		{
  2598  			description: "Extension Nil",
  2599  			userExt:     nil,
  2600  			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
  2601  				{Source: "source1", Bidders: []string{"bidderA"}},
  2602  			},
  2603  			expectedUserExt: nil,
  2604  		},
  2605  		{
  2606  			description: "Extension Empty",
  2607  			userExt:     json.RawMessage(`{}`),
  2608  			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
  2609  				{Source: "source1", Bidders: []string{"bidderA"}},
  2610  			},
  2611  			expectedUserExt: json.RawMessage(`{}`),
  2612  		},
  2613  		{
  2614  			description: "Extension Empty - Keep Other Data",
  2615  			userExt:     json.RawMessage(`{"other":42}`),
  2616  			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
  2617  				{Source: "source1", Bidders: []string{"bidderA"}},
  2618  			},
  2619  			expectedUserExt: json.RawMessage(`{"other":42}`),
  2620  		},
  2621  		{
  2622  			description: "Eids Empty",
  2623  			userExt:     json.RawMessage(`{"eids":[]}`),
  2624  			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
  2625  				{Source: "source1", Bidders: []string{"bidderA"}},
  2626  			},
  2627  			expectedUserExt: json.RawMessage(`{"eids":[]}`),
  2628  		},
  2629  		{
  2630  			description: "Eids Empty - Keep Other Data",
  2631  			userExt:     json.RawMessage(`{"eids":[],"other":42}`),
  2632  			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
  2633  				{Source: "source1", Bidders: []string{"bidderA"}},
  2634  			},
  2635  			expectedUserExt: json.RawMessage(`{"eids":[],"other":42}`),
  2636  		},
  2637  		{
  2638  			description:     "Allowed By Nil Permissions",
  2639  			userExt:         json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID"}]}]}`),
  2640  			eidPermissions:  nil,
  2641  			expectedUserExt: json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID"}]}]}`),
  2642  		},
  2643  		{
  2644  			description:     "Allowed By Empty Permissions",
  2645  			userExt:         json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID"}]}]}`),
  2646  			eidPermissions:  []openrtb_ext.ExtRequestPrebidDataEidPermission{},
  2647  			expectedUserExt: json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID"}]}]}`),
  2648  		},
  2649  		{
  2650  			description: "Allowed By Specific Bidder",
  2651  			userExt:     json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID"}]}]}`),
  2652  			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
  2653  				{Source: "source1", Bidders: []string{"bidderA"}},
  2654  			},
  2655  			expectedUserExt: json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID"}]}]}`),
  2656  		},
  2657  		{
  2658  			description: "Allowed By Specific Bidder - Case Insensitive",
  2659  			userExt:     json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID"}]}]}`),
  2660  			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
  2661  				{Source: "source1", Bidders: []string{"BIDDERA"}},
  2662  			},
  2663  			expectedUserExt: json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID"}]}]}`),
  2664  		},
  2665  		{
  2666  			description: "Allowed By All Bidders",
  2667  			userExt:     json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID"}]}]}`),
  2668  			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
  2669  				{Source: "source1", Bidders: []string{"*"}},
  2670  			},
  2671  			expectedUserExt: json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID"}]}]}`),
  2672  		},
  2673  		{
  2674  			description: "Allowed By Lack Of Matching Source",
  2675  			userExt:     json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID"}]}]}`),
  2676  			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
  2677  				{Source: "source2", Bidders: []string{"otherBidder"}},
  2678  			},
  2679  			expectedUserExt: json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID"}]}]}`),
  2680  		},
  2681  		{
  2682  			description: "Allowed - Keep Other Data",
  2683  			userExt:     json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID"}]}],"other":42}`),
  2684  			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
  2685  				{Source: "source1", Bidders: []string{"bidderA"}},
  2686  			},
  2687  			expectedUserExt: json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID"}]}],"other":42}`),
  2688  		},
  2689  		{
  2690  			description: "Denied",
  2691  			userExt:     json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID"}]}]}`),
  2692  			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
  2693  				{Source: "source1", Bidders: []string{"otherBidder"}},
  2694  			},
  2695  			expectedUserExt: nil,
  2696  		},
  2697  		{
  2698  			description: "Denied - Keep Other Data",
  2699  			userExt:     json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID"}]}],"otherdata":42}`),
  2700  			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
  2701  				{Source: "source1", Bidders: []string{"otherBidder"}},
  2702  			},
  2703  			expectedUserExt: json.RawMessage(`{"otherdata":42}`),
  2704  		},
  2705  		{
  2706  			description: "Mix Of Allowed By Specific Bidder, Allowed By Lack Of Matching Source, Denied, Keep Other Data",
  2707  			userExt:     json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID1"}]},{"source":"source2","uids":[{"id":"anyID2"}]},{"source":"source3","uids":[{"id":"anyID3"}]}],"other":42}`),
  2708  			eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
  2709  				{Source: "source1", Bidders: []string{"bidderA"}},
  2710  				{Source: "source3", Bidders: []string{"otherBidder"}},
  2711  			},
  2712  			expectedUserExt: json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID1"}]},{"source":"source2","uids":[{"id":"anyID2"}]}],"other":42}`),
  2713  		},
  2714  	}
  2715  
  2716  	for _, test := range testCases {
  2717  		request := &openrtb2.BidRequest{
  2718  			User: &openrtb2.User{Ext: test.userExt},
  2719  		}
  2720  
  2721  		requestExt := &openrtb_ext.ExtRequest{
  2722  			Prebid: openrtb_ext.ExtRequestPrebid{
  2723  				Data: &openrtb_ext.ExtRequestPrebidData{
  2724  					EidPermissions: test.eidPermissions,
  2725  				},
  2726  			},
  2727  		}
  2728  
  2729  		expectedRequest := &openrtb2.BidRequest{
  2730  			User: &openrtb2.User{Ext: test.expectedUserExt},
  2731  		}
  2732  
  2733  		resultErr := removeUnpermissionedEids(request, bidder, requestExt)
  2734  		assert.NoError(t, resultErr, test.description)
  2735  		assert.Equal(t, expectedRequest, request, test.description)
  2736  	}
  2737  }
  2738  
  2739  func TestRemoveUnpermissionedEidsUnmarshalErrors(t *testing.T) {
  2740  	testCases := []struct {
  2741  		description string
  2742  		userExt     json.RawMessage
  2743  		expectedErr string
  2744  	}{
  2745  		{
  2746  			description: "Malformed Ext",
  2747  			userExt:     json.RawMessage(`malformed`),
  2748  			expectedErr: "expect { or n, but found m",
  2749  		},
  2750  		{
  2751  			description: "Malformed Eid Array Type",
  2752  			userExt:     json.RawMessage(`{"eids":[42]}`),
  2753  			expectedErr: "cannot unmarshal []openrtb2.EID: expect { or n, but found 4",
  2754  		},
  2755  		{
  2756  			description: "Malformed Eid Item Type",
  2757  			userExt:     json.RawMessage(`{"eids":[{"source":42,"id":"anyID"}]}`),
  2758  			expectedErr: "cannot unmarshal openrtb2.EID.Source: expects \" or n, but found 4",
  2759  		},
  2760  	}
  2761  
  2762  	for _, test := range testCases {
  2763  		request := &openrtb2.BidRequest{
  2764  			User: &openrtb2.User{Ext: test.userExt},
  2765  		}
  2766  
  2767  		requestExt := &openrtb_ext.ExtRequest{
  2768  			Prebid: openrtb_ext.ExtRequestPrebid{
  2769  				Data: &openrtb_ext.ExtRequestPrebidData{
  2770  					EidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
  2771  						{Source: "source1", Bidders: []string{"*"}},
  2772  					},
  2773  				},
  2774  			},
  2775  		}
  2776  
  2777  		resultErr := removeUnpermissionedEids(request, "bidderA", requestExt)
  2778  		assert.EqualError(t, resultErr, test.expectedErr, test.description)
  2779  	}
  2780  }
  2781  
  2782  func TestGetDebugInfo(t *testing.T) {
  2783  	type testInput struct {
  2784  		debugEnabledOrOverridden bool
  2785  		accountDebugFlag         bool
  2786  	}
  2787  	type testOut struct {
  2788  		responseDebugAllow bool
  2789  		accountDebugAllow  bool
  2790  		debugLog           *DebugLog
  2791  	}
  2792  	type testCase struct {
  2793  		in       testInput
  2794  		expected testOut
  2795  	}
  2796  
  2797  	testGroups := []struct {
  2798  		description   string
  2799  		isTestRequest int8
  2800  		testCases     []testCase
  2801  	}{
  2802  		{
  2803  			description:   "Bid request doesn't call for debug info",
  2804  			isTestRequest: 0,
  2805  			testCases: []testCase{
  2806  				{
  2807  					testInput{debugEnabledOrOverridden: false, accountDebugFlag: false},
  2808  					testOut{
  2809  						responseDebugAllow: false,
  2810  						accountDebugAllow:  false,
  2811  						debugLog:           &DebugLog{Enabled: false},
  2812  					},
  2813  				},
  2814  				{
  2815  					testInput{debugEnabledOrOverridden: false, accountDebugFlag: true},
  2816  					testOut{
  2817  						responseDebugAllow: false,
  2818  						accountDebugAllow:  false,
  2819  						debugLog:           &DebugLog{Enabled: true},
  2820  					},
  2821  				},
  2822  				{
  2823  					testInput{debugEnabledOrOverridden: true, accountDebugFlag: false},
  2824  					testOut{
  2825  						responseDebugAllow: true,
  2826  						accountDebugAllow:  false,
  2827  						debugLog:           &DebugLog{DebugEnabledOrOverridden: true, Enabled: true},
  2828  					},
  2829  				},
  2830  				{
  2831  					testInput{debugEnabledOrOverridden: true, accountDebugFlag: true},
  2832  					testOut{
  2833  						responseDebugAllow: true,
  2834  						accountDebugAllow:  true,
  2835  						debugLog:           &DebugLog{DebugEnabledOrOverridden: true, Enabled: true},
  2836  					},
  2837  				},
  2838  			},
  2839  		},
  2840  		{
  2841  			description:   "Bid request requires debug info",
  2842  			isTestRequest: 1,
  2843  			testCases: []testCase{
  2844  				{
  2845  					testInput{debugEnabledOrOverridden: false, accountDebugFlag: false},
  2846  					testOut{
  2847  						responseDebugAllow: false,
  2848  						accountDebugAllow:  false,
  2849  						debugLog:           &DebugLog{Enabled: false},
  2850  					},
  2851  				},
  2852  				{
  2853  					testInput{debugEnabledOrOverridden: false, accountDebugFlag: true},
  2854  					testOut{
  2855  						responseDebugAllow: true,
  2856  						accountDebugAllow:  true,
  2857  						debugLog:           &DebugLog{Enabled: true},
  2858  					},
  2859  				},
  2860  				{
  2861  					testInput{debugEnabledOrOverridden: true, accountDebugFlag: false},
  2862  					testOut{
  2863  						responseDebugAllow: true,
  2864  						accountDebugAllow:  false,
  2865  						debugLog:           &DebugLog{DebugEnabledOrOverridden: true, Enabled: true},
  2866  					},
  2867  				},
  2868  				{
  2869  					testInput{debugEnabledOrOverridden: true, accountDebugFlag: true},
  2870  					testOut{
  2871  						responseDebugAllow: true,
  2872  						accountDebugAllow:  true,
  2873  						debugLog:           &DebugLog{DebugEnabledOrOverridden: true, Enabled: true},
  2874  					},
  2875  				},
  2876  			},
  2877  		},
  2878  	}
  2879  	for _, group := range testGroups {
  2880  		for i, tc := range group.testCases {
  2881  			inDebugLog := &DebugLog{DebugEnabledOrOverridden: tc.in.debugEnabledOrOverridden}
  2882  
  2883  			// run
  2884  			responseDebugAllow, accountDebugAllow, debugLog := getDebugInfo(group.isTestRequest, nil, tc.in.accountDebugFlag, inDebugLog)
  2885  
  2886  			// assertions
  2887  			assert.Equal(t, tc.expected.responseDebugAllow, responseDebugAllow, "%s - %d", group.description, i)
  2888  			assert.Equal(t, tc.expected.accountDebugAllow, accountDebugAllow, "%s - %d", group.description, i)
  2889  			assert.Equal(t, tc.expected.debugLog, debugLog, "%s - %d", group.description, i)
  2890  		}
  2891  	}
  2892  }
  2893  
  2894  func TestRemoveUnpermissionedEidsEmptyValidations(t *testing.T) {
  2895  	testCases := []struct {
  2896  		description string
  2897  		request     *openrtb2.BidRequest
  2898  		requestExt  *openrtb_ext.ExtRequest
  2899  	}{
  2900  		{
  2901  			description: "Nil User",
  2902  			request: &openrtb2.BidRequest{
  2903  				User: nil,
  2904  			},
  2905  			requestExt: &openrtb_ext.ExtRequest{
  2906  				Prebid: openrtb_ext.ExtRequestPrebid{
  2907  					Data: &openrtb_ext.ExtRequestPrebidData{
  2908  						EidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
  2909  							{Source: "source1", Bidders: []string{"*"}},
  2910  						},
  2911  					},
  2912  				},
  2913  			},
  2914  		},
  2915  		{
  2916  			description: "Empty User",
  2917  			request: &openrtb2.BidRequest{
  2918  				User: &openrtb2.User{},
  2919  			},
  2920  			requestExt: &openrtb_ext.ExtRequest{
  2921  				Prebid: openrtb_ext.ExtRequestPrebid{
  2922  					Data: &openrtb_ext.ExtRequestPrebidData{
  2923  						EidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{
  2924  							{Source: "source1", Bidders: []string{"*"}},
  2925  						},
  2926  					},
  2927  				},
  2928  			},
  2929  		},
  2930  		{
  2931  			description: "Nil Ext",
  2932  			request: &openrtb2.BidRequest{
  2933  				User: &openrtb2.User{Ext: json.RawMessage(`{"eids":[{"source":"source1","id":"anyID"}]}`)},
  2934  			},
  2935  			requestExt: nil,
  2936  		},
  2937  		{
  2938  			description: "Nil Prebid Data",
  2939  			request: &openrtb2.BidRequest{
  2940  				User: &openrtb2.User{Ext: json.RawMessage(`{"eids":[{"source":"source1","id":"anyID"}]}`)},
  2941  			},
  2942  			requestExt: &openrtb_ext.ExtRequest{
  2943  				Prebid: openrtb_ext.ExtRequestPrebid{
  2944  					Data: nil,
  2945  				},
  2946  			},
  2947  		},
  2948  	}
  2949  
  2950  	for _, test := range testCases {
  2951  		requestExpected := *test.request
  2952  
  2953  		resultErr := removeUnpermissionedEids(test.request, "bidderA", test.requestExt)
  2954  		assert.NoError(t, resultErr, test.description+":err")
  2955  		assert.Equal(t, &requestExpected, test.request, test.description+":request")
  2956  	}
  2957  }
  2958  
  2959  func TestCleanOpenRTBRequestsSChainMultipleBidders(t *testing.T) {
  2960  	req := &openrtb2.BidRequest{
  2961  		Site: &openrtb2.Site{},
  2962  		Source: &openrtb2.Source{
  2963  			TID: "testTID",
  2964  		},
  2965  		Imp: []openrtb2.Imp{{
  2966  			Ext: json.RawMessage(`{"prebid":{"bidder":{"appnexus": {"placementId": 1}, "axonix": { "supplyId": "123"}}}}`),
  2967  		}},
  2968  		Ext: json.RawMessage(`{"prebid":{"schains":[{ "bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller1.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}, {"bidders":["axonix"],"schain":{"complete":1,"nodes":[{"asi":"directseller2.com","sid":"00002","rid":"BidRequest2","hp":1}],"ver":"1.0"}}]}}`),
  2969  	}
  2970  
  2971  	extRequest := &openrtb_ext.ExtRequest{}
  2972  	err := jsonutil.UnmarshalValid(req.Ext, extRequest)
  2973  	assert.NoErrorf(t, err, "Error unmarshaling inExt")
  2974  
  2975  	auctionReq := AuctionRequest{
  2976  		BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: req},
  2977  		UserSyncs:         &emptyUsersync{},
  2978  		TCF2Config:        gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
  2979  	}
  2980  
  2981  	gdprPermissionsBuilder := fakePermissionsBuilder{
  2982  		permissions: &permissionsMock{
  2983  			allowAllBidders: true,
  2984  			passGeo:         true,
  2985  			passID:          true,
  2986  			activitiesError: nil,
  2987  		},
  2988  	}.Builder
  2989  
  2990  	reqSplitter := &requestSplitter{
  2991  		bidderToSyncerKey: map[string]string{},
  2992  		me:                &metrics.MetricsEngineMock{},
  2993  		privacyConfig:     config.Privacy{},
  2994  		gdprPermsBuilder:  gdprPermissionsBuilder,
  2995  		hostSChainNode:    nil,
  2996  		bidderInfo:        config.BidderInfos{},
  2997  	}
  2998  	bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo, false, map[string]float64{})
  2999  
  3000  	assert.Nil(t, errs)
  3001  	assert.Len(t, bidderRequests, 2, "Bid request count is not 2")
  3002  
  3003  	bidRequestSourceExts := map[openrtb_ext.BidderName]json.RawMessage{}
  3004  	for _, bidderRequest := range bidderRequests {
  3005  		bidRequestSourceExts[bidderRequest.BidderName] = bidderRequest.BidRequest.Source.Ext
  3006  	}
  3007  
  3008  	appnexusPrebidSchainsSchain := json.RawMessage(`{"schain":{"complete":1,"nodes":[{"asi":"directseller1.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}`)
  3009  	axonixPrebidSchainsSchain := json.RawMessage(`{"schain":{"complete":1,"nodes":[{"asi":"directseller2.com","sid":"00002","rid":"BidRequest2","hp":1}],"ver":"1.0"}}`)
  3010  	assert.Equal(t, appnexusPrebidSchainsSchain, bidRequestSourceExts["appnexus"], "Incorrect appnexus bid request schain in source.ext")
  3011  	assert.Equal(t, axonixPrebidSchainsSchain, bidRequestSourceExts["axonix"], "Incorrect axonix bid request schain in source.ext")
  3012  }
  3013  
  3014  func TestCleanOpenRTBRequestsBidAdjustment(t *testing.T) {
  3015  	tcf2Consent := "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA"
  3016  	falseValue := false
  3017  	testCases := []struct {
  3018  		description         string
  3019  		gdprAccountEnabled  *bool
  3020  		gdprHostEnabled     bool
  3021  		gdpr                string
  3022  		gdprConsent         string
  3023  		gdprScrub           bool
  3024  		permissionsError    error
  3025  		gdprDefaultValue    string
  3026  		expectPrivacyLabels metrics.PrivacyLabels
  3027  		expectError         bool
  3028  		bidAdjustmentFactor map[string]float64
  3029  		expectedImp         []openrtb2.Imp
  3030  	}{
  3031  		{
  3032  			description:        "BidFloor Adjustment Done for Appnexus",
  3033  			gdprAccountEnabled: &falseValue,
  3034  			gdprHostEnabled:    true,
  3035  			gdpr:               "1",
  3036  			gdprConsent:        tcf2Consent,
  3037  			gdprScrub:          false,
  3038  			gdprDefaultValue:   "1",
  3039  			expectPrivacyLabels: metrics.PrivacyLabels{
  3040  				GDPREnforced:   false,
  3041  				GDPRTCFVersion: "",
  3042  			},
  3043  			bidAdjustmentFactor: map[string]float64{"appnexus": 0.50},
  3044  			expectedImp: []openrtb2.Imp{{
  3045  				BidFloor: 200,
  3046  				ID:       "some-imp-id",
  3047  				Banner: &openrtb2.Banner{
  3048  					Format: []openrtb2.Format{{
  3049  						W: 300,
  3050  						H: 250,
  3051  					}, {
  3052  						W: 300,
  3053  						H: 600,
  3054  					}},
  3055  				},
  3056  				Ext: json.RawMessage(`{"bidder":{"placementId": 1}}`),
  3057  			}},
  3058  		},
  3059  		{
  3060  			description:        "bidAdjustment Not provided",
  3061  			gdprAccountEnabled: &falseValue,
  3062  			gdprHostEnabled:    true,
  3063  			gdpr:               "1",
  3064  			gdprConsent:        tcf2Consent,
  3065  			gdprScrub:          false,
  3066  			gdprDefaultValue:   "1",
  3067  			expectPrivacyLabels: metrics.PrivacyLabels{
  3068  				GDPREnforced:   false,
  3069  				GDPRTCFVersion: "",
  3070  			},
  3071  			bidAdjustmentFactor: map[string]float64{},
  3072  			expectedImp: []openrtb2.Imp{{
  3073  				BidFloor: 100,
  3074  				ID:       "some-imp-id",
  3075  				Banner: &openrtb2.Banner{
  3076  					Format: []openrtb2.Format{{
  3077  						W: 300,
  3078  						H: 250,
  3079  					}, {
  3080  						W: 300,
  3081  						H: 600,
  3082  					}},
  3083  				},
  3084  				Ext: json.RawMessage(`{"bidder":{"placementId": 1}}`),
  3085  			}},
  3086  		},
  3087  	}
  3088  	for _, test := range testCases {
  3089  		req := newBidRequest(t)
  3090  		accountConfig := config.Account{
  3091  			GDPR: config.AccountGDPR{
  3092  				Enabled: &falseValue,
  3093  			},
  3094  			PriceFloors: config.AccountPriceFloors{
  3095  				AdjustForBidAdjustment: true,
  3096  			},
  3097  		}
  3098  		auctionReq := AuctionRequest{
  3099  			BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: req},
  3100  			UserSyncs:         &emptyUsersync{},
  3101  			Account:           accountConfig,
  3102  			TCF2Config:        gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
  3103  		}
  3104  		gdprPermissionsBuilder := fakePermissionsBuilder{
  3105  			permissions: &permissionsMock{
  3106  				allowAllBidders: true,
  3107  				passGeo:         !test.gdprScrub,
  3108  				passID:          !test.gdprScrub,
  3109  				activitiesError: test.permissionsError,
  3110  			},
  3111  		}.Builder
  3112  		reqSplitter := &requestSplitter{
  3113  			bidderToSyncerKey: map[string]string{},
  3114  			me:                &metrics.MetricsEngineMock{},
  3115  			gdprPermsBuilder:  gdprPermissionsBuilder,
  3116  			hostSChainNode:    nil,
  3117  			bidderInfo:        config.BidderInfos{},
  3118  		}
  3119  		results, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, false, test.bidAdjustmentFactor)
  3120  		result := results[0]
  3121  		assert.Nil(t, errs)
  3122  		assert.Equal(t, test.expectedImp, result.BidRequest.Imp, test.description)
  3123  	}
  3124  }
  3125  
  3126  func TestApplyFPD(t *testing.T) {
  3127  	testCases := []struct {
  3128  		description               string
  3129  		inputFpd                  map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData
  3130  		inputBidderName           string
  3131  		inputBidderCoreName       string
  3132  		inputBidderIsRequestAlias bool
  3133  		inputRequest              openrtb2.BidRequest
  3134  		expectedRequest           openrtb2.BidRequest
  3135  	}{
  3136  		{
  3137  			description:               "fpd-nil",
  3138  			inputFpd:                  nil,
  3139  			inputBidderName:           "bidderFromRequest",
  3140  			inputBidderCoreName:       "bidderNormalized",
  3141  			inputBidderIsRequestAlias: false,
  3142  			inputRequest:              openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}},
  3143  			expectedRequest:           openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}},
  3144  		},
  3145  		{
  3146  			description: "fpd-bidderdata-nil",
  3147  			inputFpd: map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData{
  3148  				"bidderNormalized": nil,
  3149  			},
  3150  			inputBidderName:           "bidderFromRequest",
  3151  			inputBidderCoreName:       "bidderNormalized",
  3152  			inputBidderIsRequestAlias: false,
  3153  			inputRequest:              openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}},
  3154  			expectedRequest:           openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}},
  3155  		},
  3156  		{
  3157  			description: "fpd-bidderdata-notdefined",
  3158  			inputFpd: map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData{
  3159  				"differentBidder": {App: &openrtb2.App{ID: "AppId"}},
  3160  			},
  3161  			inputBidderName:           "bidderFromRequest",
  3162  			inputBidderCoreName:       "bidderNormalized",
  3163  			inputBidderIsRequestAlias: false,
  3164  			inputRequest:              openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}},
  3165  			expectedRequest:           openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}},
  3166  		},
  3167  		{
  3168  			description: "fpd-bidderdata-alias",
  3169  			inputFpd: map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData{
  3170  				"alias": {App: &openrtb2.App{ID: "AppId"}},
  3171  			},
  3172  			inputBidderName:           "alias",
  3173  			inputBidderCoreName:       "bidder",
  3174  			inputBidderIsRequestAlias: true,
  3175  			inputRequest:              openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}},
  3176  			expectedRequest:           openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}},
  3177  		},
  3178  		{
  3179  			description: "req.Site defined; bidderFPD.Site not defined; expect request.Site remains the same",
  3180  			inputFpd: map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData{
  3181  				"bidderNormalized": {Site: nil, App: nil, User: nil},
  3182  			},
  3183  			inputBidderName:           "bidderFromRequest",
  3184  			inputBidderCoreName:       "bidderNormalized",
  3185  			inputBidderIsRequestAlias: false,
  3186  			inputRequest:              openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}},
  3187  			expectedRequest:           openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}},
  3188  		},
  3189  		{
  3190  			description: "req.Site, req.App, req.User are not defined; bidderFPD.App, bidderFPD.Site and bidderFPD.User defined; " +
  3191  				"expect req.Site, req.App, req.User to be overriden by bidderFPD.App, bidderFPD.Site and bidderFPD.User",
  3192  			inputFpd: map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData{
  3193  				"bidderNormalized": {Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}, User: &openrtb2.User{ID: "UserId"}},
  3194  			},
  3195  			inputBidderName:           "bidderFromRequest",
  3196  			inputBidderCoreName:       "bidderNormalized",
  3197  			inputBidderIsRequestAlias: false,
  3198  			inputRequest:              openrtb2.BidRequest{},
  3199  			expectedRequest:           openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}, User: &openrtb2.User{ID: "UserId"}},
  3200  		},
  3201  		{
  3202  			description: "req.Site, defined; bidderFPD.App defined; expect request.App to be overriden by bidderFPD.App; expect req.Site remains the same",
  3203  			inputFpd: map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData{
  3204  				"bidderNormalized": {App: &openrtb2.App{ID: "AppId"}},
  3205  			},
  3206  			inputBidderName:           "bidderFromRequest",
  3207  			inputBidderCoreName:       "bidderNormalized",
  3208  			inputBidderIsRequestAlias: false,
  3209  			inputRequest:              openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}},
  3210  			expectedRequest:           openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}},
  3211  		},
  3212  		{
  3213  			description: "req.Site, req.App defined; bidderFPD.App defined; expect request.App to be overriden by bidderFPD.App",
  3214  			inputFpd: map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData{
  3215  				"bidderNormalized": {App: &openrtb2.App{ID: "AppId"}},
  3216  			},
  3217  			inputBidderName:           "bidderFromRequest",
  3218  			inputBidderCoreName:       "bidderNormalized",
  3219  			inputBidderIsRequestAlias: false,
  3220  			inputRequest:              openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "TestAppId"}},
  3221  			expectedRequest:           openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}},
  3222  		},
  3223  		{
  3224  			description: "req.User is defined; bidderFPD.User defined; req.User has BuyerUID. Expect to see user.BuyerUID in result request",
  3225  			inputFpd: map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData{
  3226  				"bidderNormalized": {Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}, User: &openrtb2.User{ID: "UserId"}},
  3227  			},
  3228  			inputBidderName:           "bidderFromRequest",
  3229  			inputBidderCoreName:       "bidderNormalized",
  3230  			inputBidderIsRequestAlias: false,
  3231  			inputRequest:              openrtb2.BidRequest{User: &openrtb2.User{ID: "UserIdIn", BuyerUID: "12345"}},
  3232  			expectedRequest:           openrtb2.BidRequest{User: &openrtb2.User{ID: "UserId", BuyerUID: "12345"}, Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}},
  3233  		},
  3234  		{
  3235  			description: "req.User is defined; bidderFPD.User defined; req.User has BuyerUID with zero length. Expect to see empty user.BuyerUID in result request",
  3236  			inputFpd: map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData{
  3237  				"bidderNormalized": {Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}, User: &openrtb2.User{ID: "UserId"}},
  3238  			},
  3239  			inputBidderName:           "bidderFromRequest",
  3240  			inputBidderCoreName:       "bidderNormalized",
  3241  			inputBidderIsRequestAlias: false,
  3242  			inputRequest:              openrtb2.BidRequest{User: &openrtb2.User{ID: "UserIdIn", BuyerUID: ""}},
  3243  			expectedRequest:           openrtb2.BidRequest{User: &openrtb2.User{ID: "UserId"}, Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}},
  3244  		},
  3245  		{
  3246  			description: "req.User is not defined; bidderFPD.User defined and has BuyerUID. Expect to see user.BuyerUID in result request",
  3247  			inputFpd: map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData{
  3248  				"bidderNormalized": {Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}, User: &openrtb2.User{ID: "UserId", BuyerUID: "FPDBuyerUID"}},
  3249  			},
  3250  			inputBidderName:           "bidderFromRequest",
  3251  			inputBidderCoreName:       "bidderNormalized",
  3252  			inputBidderIsRequestAlias: false,
  3253  			inputRequest:              openrtb2.BidRequest{},
  3254  			expectedRequest:           openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}, User: &openrtb2.User{ID: "UserId", BuyerUID: "FPDBuyerUID"}},
  3255  		},
  3256  	}
  3257  
  3258  	for _, testCase := range testCases {
  3259  		bidderRequest := BidderRequest{
  3260  			BidderName:     openrtb_ext.BidderName(testCase.inputBidderName),
  3261  			BidderCoreName: openrtb_ext.BidderName(testCase.inputBidderCoreName),
  3262  			IsRequestAlias: testCase.inputBidderIsRequestAlias,
  3263  			BidRequest:     &testCase.inputRequest,
  3264  		}
  3265  		applyFPD(testCase.inputFpd, bidderRequest)
  3266  		assert.Equal(t, testCase.expectedRequest, testCase.inputRequest, fmt.Sprintf("incorrect request after applying fpd, testcase %s", testCase.description))
  3267  	}
  3268  }
  3269  
  3270  func TestGetRequestAliases(t *testing.T) {
  3271  	tests := []struct {
  3272  		name         string
  3273  		givenRequest openrtb_ext.RequestWrapper
  3274  		wantAliases  map[string]string
  3275  		wantGVLIDs   map[string]uint16
  3276  		wantError    string
  3277  	}{
  3278  		{
  3279  			name: "nil",
  3280  			givenRequest: openrtb_ext.RequestWrapper{
  3281  				BidRequest: &openrtb2.BidRequest{},
  3282  			},
  3283  			wantAliases: nil,
  3284  			wantGVLIDs:  nil,
  3285  			wantError:   "",
  3286  		},
  3287  		{
  3288  			name: "empty",
  3289  			givenRequest: openrtb_ext.RequestWrapper{
  3290  				BidRequest: &openrtb2.BidRequest{
  3291  					Ext: json.RawMessage(`{}`),
  3292  				},
  3293  			},
  3294  			wantAliases: nil,
  3295  			wantGVLIDs:  nil,
  3296  			wantError:   "",
  3297  		},
  3298  		{
  3299  			name: "empty-prebid",
  3300  			givenRequest: openrtb_ext.RequestWrapper{
  3301  				BidRequest: &openrtb2.BidRequest{
  3302  					Ext: json.RawMessage(`{"prebid":{}}`),
  3303  				},
  3304  			},
  3305  			wantAliases: nil,
  3306  			wantGVLIDs:  nil,
  3307  			wantError:   "",
  3308  		},
  3309  		{
  3310  			name: "aliases-and-gvlids",
  3311  			givenRequest: openrtb_ext.RequestWrapper{
  3312  				BidRequest: &openrtb2.BidRequest{
  3313  					Ext: json.RawMessage(`{"prebid":{"aliases":{"alias1":"bidder1"}, "aliasgvlids":{"alias1":1}}}`),
  3314  				},
  3315  			},
  3316  			wantAliases: map[string]string{"alias1": "bidder1"},
  3317  			wantGVLIDs:  map[string]uint16{"alias1": 1},
  3318  			wantError:   "",
  3319  		},
  3320  		{
  3321  			name: "malformed",
  3322  			givenRequest: openrtb_ext.RequestWrapper{
  3323  				BidRequest: &openrtb2.BidRequest{
  3324  					Ext: json.RawMessage(`malformed`),
  3325  				},
  3326  			},
  3327  			wantAliases: nil,
  3328  			wantGVLIDs:  nil,
  3329  			wantError:   "request.ext is invalid",
  3330  		},
  3331  	}
  3332  	for _, test := range tests {
  3333  		t.Run(test.name, func(t *testing.T) {
  3334  			gotAliases, gotGVLIDs, err := getRequestAliases(&test.givenRequest)
  3335  
  3336  			assert.Equal(t, test.wantAliases, gotAliases, "aliases")
  3337  			assert.Equal(t, test.wantGVLIDs, gotGVLIDs, "gvlids")
  3338  
  3339  			if len(test.wantError) > 0 {
  3340  				require.Len(t, err, 1, "error-len")
  3341  				assert.EqualError(t, err[0], test.wantError, "error")
  3342  			} else {
  3343  				assert.Empty(t, err, "error")
  3344  			}
  3345  		})
  3346  	}
  3347  }
  3348  
  3349  func TestBuildExtData(t *testing.T) {
  3350  	testCases := []struct {
  3351  		description string
  3352  		input       []byte
  3353  		expectedRes string
  3354  	}{
  3355  		{
  3356  			description: "Input object with int value",
  3357  			input:       []byte(`{"someData": 123}`),
  3358  			expectedRes: `{"data": {"someData": 123}}`,
  3359  		},
  3360  		{
  3361  			description: "Input object with bool value",
  3362  			input:       []byte(`{"someData": true}`),
  3363  			expectedRes: `{"data": {"someData": true}}`,
  3364  		},
  3365  		{
  3366  			description: "Input object with string value",
  3367  			input:       []byte(`{"someData": "true"}`),
  3368  			expectedRes: `{"data": {"someData": "true"}}`,
  3369  		},
  3370  		{
  3371  			description: "No input object",
  3372  			input:       []byte(`{}`),
  3373  			expectedRes: `{"data": {}}`,
  3374  		},
  3375  		{
  3376  			description: "Input object with object value",
  3377  			input:       []byte(`{"someData": {"moreFpdData": "fpddata"}}`),
  3378  			expectedRes: `{"data": {"someData": {"moreFpdData": "fpddata"}}}`,
  3379  		},
  3380  	}
  3381  
  3382  	for _, test := range testCases {
  3383  		actualRes := WrapJSONInData(test.input)
  3384  		assert.JSONEq(t, test.expectedRes, string(actualRes), "Incorrect result data")
  3385  	}
  3386  }
  3387  
  3388  func TestCleanOpenRTBRequestsFilterBidderRequestExt(t *testing.T) {
  3389  	testCases := []struct {
  3390  		desc      string
  3391  		inExt     json.RawMessage
  3392  		inCfgABC  *openrtb_ext.ExtAlternateBidderCodes
  3393  		wantExt   []json.RawMessage
  3394  		wantError bool
  3395  	}{
  3396  		{
  3397  			desc:      "Nil request ext, default account alternatebiddercodes config (nil)",
  3398  			inExt:     nil,
  3399  			inCfgABC:  nil,
  3400  			wantExt:   nil,
  3401  			wantError: false,
  3402  		},
  3403  		{
  3404  			desc:     "Nil request ext, default account alternatebiddercodes config (explicity defined)",
  3405  			inExt:    nil,
  3406  			inCfgABC: &openrtb_ext.ExtAlternateBidderCodes{Enabled: false},
  3407  			wantExt: []json.RawMessage{
  3408  				json.RawMessage(`{"prebid":{"alternatebiddercodes":{"enabled":false,"bidders":null}}}`),
  3409  				json.RawMessage(`{"prebid":{"alternatebiddercodes":{"enabled":false,"bidders":null}}}`),
  3410  			},
  3411  			wantError: false,
  3412  		},
  3413  		{
  3414  			desc:     "request ext, default account alternatebiddercodes config (explicity defined)",
  3415  			inExt:    json.RawMessage(`{"prebid":{}}`),
  3416  			inCfgABC: &openrtb_ext.ExtAlternateBidderCodes{Enabled: false},
  3417  			wantExt: []json.RawMessage{
  3418  				json.RawMessage(`{"prebid":{"alternatebiddercodes":{"enabled":false,"bidders":null}}}`),
  3419  				json.RawMessage(`{"prebid":{"alternatebiddercodes":{"enabled":false,"bidders":null}}}`),
  3420  			},
  3421  			wantError: false,
  3422  		},
  3423  		{
  3424  			desc:  "Nil request ext, account alternatebiddercodes config disabled with biddercodes defined",
  3425  			inExt: nil,
  3426  			inCfgABC: &openrtb_ext.ExtAlternateBidderCodes{
  3427  				Enabled: false,
  3428  				Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
  3429  					"pubmatic": {Enabled: true},
  3430  				},
  3431  			},
  3432  			wantExt: []json.RawMessage{
  3433  				json.RawMessage(`{"prebid":{"alternatebiddercodes":{"enabled":false,"bidders":null}}}`),
  3434  				json.RawMessage(`{"prebid":{"alternatebiddercodes":{"enabled":false,"bidders":{"pubmatic":{"enabled":true,"allowedbiddercodes":null}}}}}`),
  3435  			},
  3436  			wantError: false,
  3437  		},
  3438  		{
  3439  			desc:  "Nil request ext, account alternatebiddercodes config disabled with biddercodes defined (not participant bidder)",
  3440  			inExt: nil,
  3441  			inCfgABC: &openrtb_ext.ExtAlternateBidderCodes{
  3442  				Enabled: false,
  3443  				Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
  3444  					"ix": {Enabled: true},
  3445  				},
  3446  			},
  3447  			wantExt: []json.RawMessage{
  3448  				json.RawMessage(`{"prebid":{"alternatebiddercodes":{"enabled":false,"bidders":null}}}`),
  3449  				json.RawMessage(`{"prebid":{"alternatebiddercodes":{"enabled":false,"bidders":null}}}`),
  3450  			},
  3451  			wantError: false,
  3452  		},
  3453  		{
  3454  			desc:     "Nil request ext, alternatebiddercodes config enabled but bidder not present",
  3455  			inExt:    nil,
  3456  			inCfgABC: &openrtb_ext.ExtAlternateBidderCodes{Enabled: true},
  3457  			wantExt: []json.RawMessage{
  3458  				json.RawMessage(`{"prebid":{"alternatebiddercodes":{"enabled":true,"bidders":null}}}`),
  3459  				json.RawMessage(`{"prebid":{"alternatebiddercodes":{"enabled":true,"bidders":null}}}`),
  3460  			},
  3461  			wantError: false,
  3462  		},
  3463  		{
  3464  			desc:      "request ext with default alternatebiddercodes values (nil)",
  3465  			inExt:     json.RawMessage(`{"prebid":{}}`),
  3466  			inCfgABC:  nil,
  3467  			wantExt:   nil,
  3468  			wantError: false,
  3469  		},
  3470  		{
  3471  			desc:     "request ext w/o alternatebiddercodes",
  3472  			inExt:    json.RawMessage(`{"prebid":{}}`),
  3473  			inCfgABC: &openrtb_ext.ExtAlternateBidderCodes{},
  3474  			wantExt: []json.RawMessage{
  3475  				json.RawMessage(`{"prebid":{"alternatebiddercodes":{"enabled":false,"bidders":null}}}`),
  3476  				json.RawMessage(`{"prebid":{"alternatebiddercodes":{"enabled":false,"bidders":null}}}`),
  3477  			},
  3478  			wantError: false,
  3479  		},
  3480  		{
  3481  			desc:     "request ext having alternatebiddercodes for only one bidder",
  3482  			inExt:    json.RawMessage(`{"prebid":{"alternatebiddercodes":{"enabled":true,"bidders":{"pubmatic":{"enabled":true,"allowedbiddercodes":["groupm"]}}}}}`),
  3483  			inCfgABC: &openrtb_ext.ExtAlternateBidderCodes{Enabled: false},
  3484  			wantExt: []json.RawMessage{
  3485  				json.RawMessage(`{"prebid":{"alternatebiddercodes":{"enabled":true,"bidders":null}}}`),
  3486  				json.RawMessage(`{"prebid":{"alternatebiddercodes":{"enabled":true,"bidders":{"pubmatic":{"enabled":true,"allowedbiddercodes":["groupm"]}}}}}`),
  3487  			},
  3488  			wantError: false,
  3489  		},
  3490  		{
  3491  			desc:     "request ext having alternatebiddercodes for multiple bidder",
  3492  			inExt:    json.RawMessage(`{"prebid":{"alternatebiddercodes":{"enabled":true,"bidders":{"pubmatic":{"enabled":true,"allowedbiddercodes":["groupm"]},"appnexus":{"enabled":true,"allowedbiddercodes":["ix"]}}}}}`),
  3493  			inCfgABC: &openrtb_ext.ExtAlternateBidderCodes{Enabled: false},
  3494  			wantExt: []json.RawMessage{
  3495  				json.RawMessage(`{"prebid":{"alternatebiddercodes":{"enabled":true,"bidders":{"appnexus":{"enabled":true,"allowedbiddercodes":["ix"]}}}}}`),
  3496  				json.RawMessage(`{"prebid":{"alternatebiddercodes":{"enabled":true,"bidders":{"pubmatic":{"enabled":true,"allowedbiddercodes":["groupm"]}}}}}`),
  3497  			},
  3498  			wantError: false,
  3499  		},
  3500  		{
  3501  			desc:     "request ext having alternatebiddercodes for multiple bidder (config alternatebiddercodes not defined)",
  3502  			inExt:    json.RawMessage(`{"prebid":{"alternatebiddercodes":{"enabled":true,"bidders":{"pubmatic":{"enabled":true,"allowedbiddercodes":["groupm"]},"appnexus":{"enabled":true,"allowedbiddercodes":["ix"]}}}}}`),
  3503  			inCfgABC: &openrtb_ext.ExtAlternateBidderCodes{Enabled: false},
  3504  			wantExt: []json.RawMessage{
  3505  				json.RawMessage(`{"prebid":{"alternatebiddercodes":{"enabled":true,"bidders":{"appnexus":{"enabled":true,"allowedbiddercodes":["ix"]}}}}}`),
  3506  				json.RawMessage(`{"prebid":{"alternatebiddercodes":{"enabled":true,"bidders":{"pubmatic":{"enabled":true,"allowedbiddercodes":["groupm"]}}}}}`),
  3507  			},
  3508  			wantError: false,
  3509  		},
  3510  		{
  3511  			desc:  "Nil request ext, alternatebiddercodes config enabled with bidder code for only one bidder",
  3512  			inExt: nil,
  3513  			inCfgABC: &openrtb_ext.ExtAlternateBidderCodes{
  3514  				Enabled: true,
  3515  				Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
  3516  					"pubmatic": {
  3517  						Enabled:            true,
  3518  						AllowedBidderCodes: []string{"groupm"},
  3519  					},
  3520  				},
  3521  			},
  3522  			wantExt: []json.RawMessage{
  3523  				json.RawMessage(`{"prebid":{"alternatebiddercodes":{"enabled":true,"bidders":null}}}`),
  3524  				json.RawMessage(`{"prebid":{"alternatebiddercodes":{"enabled":true,"bidders":{"pubmatic":{"enabled":true,"allowedbiddercodes":["groupm"]}}}}}`),
  3525  			},
  3526  			wantError: false,
  3527  		},
  3528  	}
  3529  
  3530  	for _, test := range testCases {
  3531  		req := newBidRequestWithBidderParams(t)
  3532  		req.Ext = nil
  3533  		var extRequest *openrtb_ext.ExtRequest
  3534  		if test.inExt != nil {
  3535  			req.Ext = test.inExt
  3536  			extRequest = &openrtb_ext.ExtRequest{}
  3537  			err := jsonutil.UnmarshalValid(req.Ext, extRequest)
  3538  			assert.NoErrorf(t, err, test.desc+":Error unmarshaling inExt")
  3539  		}
  3540  
  3541  		auctionReq := AuctionRequest{
  3542  			BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: req},
  3543  			UserSyncs:         &emptyUsersync{},
  3544  			Account:           config.Account{AlternateBidderCodes: test.inCfgABC},
  3545  			TCF2Config:        gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
  3546  		}
  3547  		gdprPermissionsBuilder := fakePermissionsBuilder{
  3548  			permissions: &permissionsMock{
  3549  				allowAllBidders: true,
  3550  			},
  3551  		}.Builder
  3552  
  3553  		reqSplitter := &requestSplitter{
  3554  			bidderToSyncerKey: map[string]string{},
  3555  			me:                &metrics.MetricsEngineMock{},
  3556  			privacyConfig:     config.Privacy{},
  3557  			gdprPermsBuilder:  gdprPermissionsBuilder,
  3558  			hostSChainNode:    nil,
  3559  			bidderInfo:        config.BidderInfos{},
  3560  		}
  3561  
  3562  		bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo, false, map[string]float64{})
  3563  		assert.Equal(t, test.wantError, len(errs) != 0, test.desc)
  3564  		sort.Slice(bidderRequests, func(i, j int) bool {
  3565  			return bidderRequests[i].BidderCoreName < bidderRequests[j].BidderCoreName
  3566  		})
  3567  		for i, wantBidderRequest := range test.wantExt {
  3568  			assert.Equal(t, wantBidderRequest, bidderRequests[i].BidRequest.Ext, test.desc+" : "+string(bidderRequests[i].BidderCoreName)+"\n\t\tGotRequestExt : "+string(bidderRequests[i].BidRequest.Ext))
  3569  		}
  3570  	}
  3571  }
  3572  
  3573  type GPPMockSection struct {
  3574  	sectionID constants.SectionID
  3575  	value     string
  3576  }
  3577  
  3578  func (gs GPPMockSection) GetID() constants.SectionID {
  3579  	return gs.sectionID
  3580  }
  3581  
  3582  func (gs GPPMockSection) GetValue() string {
  3583  	return gs.value
  3584  }
  3585  
  3586  func (gs GPPMockSection) Encode(bool) []byte {
  3587  	return nil
  3588  }
  3589  
  3590  func TestGdprFromGPP(t *testing.T) {
  3591  	testCases := []struct {
  3592  		name            string
  3593  		initialRequest  *openrtb2.BidRequest
  3594  		gpp             gpplib.GppContainer
  3595  		expectedRequest *openrtb2.BidRequest
  3596  	}{
  3597  		{
  3598  			name:            "Empty", // Empty Request
  3599  			initialRequest:  &openrtb2.BidRequest{},
  3600  			gpp:             gpplib.GppContainer{},
  3601  			expectedRequest: &openrtb2.BidRequest{},
  3602  		},
  3603  		{
  3604  			name: "GDPR_Downgrade", // GDPR from GPP, into empty
  3605  			initialRequest: &openrtb2.BidRequest{
  3606  				Regs: &openrtb2.Regs{
  3607  					GPPSID: []int8{2},
  3608  				},
  3609  			},
  3610  			gpp: gpplib.GppContainer{
  3611  				SectionTypes: []constants.SectionID{2},
  3612  				Sections: []gpplib.Section{
  3613  					GPPMockSection{
  3614  						sectionID: 2,
  3615  						value:     "GDPRConsent",
  3616  					},
  3617  				},
  3618  			},
  3619  			expectedRequest: &openrtb2.BidRequest{
  3620  				Regs: &openrtb2.Regs{
  3621  					GPPSID: []int8{2},
  3622  					GDPR:   ptrutil.ToPtr[int8](1),
  3623  				},
  3624  				User: &openrtb2.User{
  3625  					Consent: "GDPRConsent",
  3626  				},
  3627  			},
  3628  		},
  3629  		{
  3630  			name: "GDPR_Downgrade", // GDPR from GPP, into empty legacy, existing objects
  3631  			initialRequest: &openrtb2.BidRequest{
  3632  				Regs: &openrtb2.Regs{
  3633  					GPPSID:    []int8{2},
  3634  					USPrivacy: "LegacyUSP",
  3635  				},
  3636  				User: &openrtb2.User{
  3637  					ID: "1234",
  3638  				},
  3639  			},
  3640  			gpp: gpplib.GppContainer{
  3641  				SectionTypes: []constants.SectionID{2},
  3642  				Sections: []gpplib.Section{
  3643  					GPPMockSection{
  3644  						sectionID: 2,
  3645  						value:     "GDPRConsent",
  3646  					},
  3647  				},
  3648  			},
  3649  			expectedRequest: &openrtb2.BidRequest{
  3650  				Regs: &openrtb2.Regs{
  3651  					GPPSID:    []int8{2},
  3652  					GDPR:      ptrutil.ToPtr[int8](1),
  3653  					USPrivacy: "LegacyUSP",
  3654  				},
  3655  				User: &openrtb2.User{
  3656  					ID:      "1234",
  3657  					Consent: "GDPRConsent",
  3658  				},
  3659  			},
  3660  		},
  3661  		{
  3662  			name: "Downgrade_Blocked_By_Existing", // GDPR from GPP blocked by existing GDPR",
  3663  			initialRequest: &openrtb2.BidRequest{
  3664  				Regs: &openrtb2.Regs{
  3665  					GPPSID: []int8{2},
  3666  					GDPR:   ptrutil.ToPtr[int8](1),
  3667  				},
  3668  				User: &openrtb2.User{
  3669  					Consent: "LegacyConsent",
  3670  				},
  3671  			},
  3672  			gpp: gpplib.GppContainer{
  3673  				SectionTypes: []constants.SectionID{2},
  3674  				Sections: []gpplib.Section{
  3675  					GPPMockSection{
  3676  						sectionID: 2,
  3677  						value:     "GDPRConsent",
  3678  					},
  3679  				},
  3680  			},
  3681  			expectedRequest: &openrtb2.BidRequest{
  3682  				Regs: &openrtb2.Regs{
  3683  					GPPSID: []int8{2},
  3684  					GDPR:   ptrutil.ToPtr[int8](1),
  3685  				},
  3686  				User: &openrtb2.User{
  3687  					Consent: "LegacyConsent",
  3688  				},
  3689  			},
  3690  		},
  3691  		{
  3692  			name: "Downgrade_Partial", // GDPR from GPP partially blocked by existing GDPR
  3693  			initialRequest: &openrtb2.BidRequest{
  3694  				Regs: &openrtb2.Regs{
  3695  					GPPSID: []int8{2},
  3696  					GDPR:   ptrutil.ToPtr[int8](0),
  3697  				},
  3698  			},
  3699  			gpp: gpplib.GppContainer{
  3700  				SectionTypes: []constants.SectionID{2},
  3701  				Sections: []gpplib.Section{
  3702  					GPPMockSection{
  3703  						sectionID: 2,
  3704  						value:     "GDPRConsent",
  3705  					},
  3706  				},
  3707  			},
  3708  			expectedRequest: &openrtb2.BidRequest{
  3709  				Regs: &openrtb2.Regs{
  3710  					GPPSID: []int8{2},
  3711  					GDPR:   ptrutil.ToPtr[int8](0),
  3712  				},
  3713  				User: &openrtb2.User{
  3714  					Consent: "GDPRConsent",
  3715  				},
  3716  			},
  3717  		},
  3718  		{
  3719  			name: "No_GDPR", // Downgrade not possible due to missing GDPR
  3720  			initialRequest: &openrtb2.BidRequest{
  3721  				Regs: &openrtb2.Regs{
  3722  					GPPSID: []int8{6},
  3723  				},
  3724  			},
  3725  			gpp: gpplib.GppContainer{
  3726  				SectionTypes: []constants.SectionID{6},
  3727  				Sections: []gpplib.Section{
  3728  					GPPMockSection{
  3729  						sectionID: 6,
  3730  						value:     "USPrivacy",
  3731  					},
  3732  				},
  3733  			},
  3734  			expectedRequest: &openrtb2.BidRequest{
  3735  				Regs: &openrtb2.Regs{
  3736  					GPPSID: []int8{6},
  3737  					GDPR:   ptrutil.ToPtr[int8](0),
  3738  				},
  3739  			},
  3740  		},
  3741  		{
  3742  			name: "No_SID", // GDPR from GPP partially blocked by no SID
  3743  			initialRequest: &openrtb2.BidRequest{
  3744  				Regs: &openrtb2.Regs{
  3745  					GPPSID: []int8{6},
  3746  				},
  3747  			},
  3748  			gpp: gpplib.GppContainer{
  3749  				SectionTypes: []constants.SectionID{2, 6},
  3750  				Sections: []gpplib.Section{
  3751  					GPPMockSection{
  3752  						sectionID: 2,
  3753  						value:     "GDPRConsent",
  3754  					},
  3755  					GPPMockSection{
  3756  						sectionID: 6,
  3757  						value:     "USPrivacy",
  3758  					},
  3759  				},
  3760  			},
  3761  			expectedRequest: &openrtb2.BidRequest{
  3762  				Regs: &openrtb2.Regs{
  3763  					GPPSID: []int8{6},
  3764  					GDPR:   ptrutil.ToPtr[int8](0),
  3765  				},
  3766  				User: &openrtb2.User{
  3767  					Consent: "GDPRConsent",
  3768  				},
  3769  			},
  3770  		},
  3771  		{
  3772  			name:           "GDPR_Nil_SID", // GDPR from GPP, into empty, but with nil SID
  3773  			initialRequest: &openrtb2.BidRequest{},
  3774  			gpp: gpplib.GppContainer{
  3775  				SectionTypes: []constants.SectionID{2},
  3776  				Sections: []gpplib.Section{
  3777  					GPPMockSection{
  3778  						sectionID: 2,
  3779  						value:     "GDPRConsent",
  3780  					},
  3781  				},
  3782  			},
  3783  			expectedRequest: &openrtb2.BidRequest{
  3784  				User: &openrtb2.User{
  3785  					Consent: "GDPRConsent",
  3786  				},
  3787  			},
  3788  		},
  3789  		{
  3790  			name: "Downgrade_Nil_SID_Blocked_By_Existing", // GDPR from GPP blocked by existing GDPR, with nil SID",
  3791  			initialRequest: &openrtb2.BidRequest{
  3792  				Regs: &openrtb2.Regs{
  3793  					GDPR: ptrutil.ToPtr[int8](1),
  3794  				},
  3795  				User: &openrtb2.User{
  3796  					Consent: "LegacyConsent",
  3797  				},
  3798  			},
  3799  			gpp: gpplib.GppContainer{
  3800  				SectionTypes: []constants.SectionID{2},
  3801  				Sections: []gpplib.Section{
  3802  					GPPMockSection{
  3803  						sectionID: 2,
  3804  						value:     "GDPRConsent",
  3805  					},
  3806  				},
  3807  			},
  3808  			expectedRequest: &openrtb2.BidRequest{
  3809  				Regs: &openrtb2.Regs{
  3810  					GDPR: ptrutil.ToPtr[int8](1),
  3811  				},
  3812  				User: &openrtb2.User{
  3813  					Consent: "LegacyConsent",
  3814  				},
  3815  			},
  3816  		},
  3817  	}
  3818  
  3819  	for _, test := range testCases {
  3820  		t.Run(test.name, func(t *testing.T) {
  3821  			setLegacyGDPRFromGPP(test.initialRequest, test.gpp)
  3822  			assert.Equal(t, test.expectedRequest, test.initialRequest)
  3823  		})
  3824  	}
  3825  }
  3826  
  3827  func TestPrivacyFromGPP(t *testing.T) {
  3828  	testCases := []struct {
  3829  		name            string
  3830  		initialRequest  *openrtb2.BidRequest
  3831  		gpp             gpplib.GppContainer
  3832  		expectedRequest *openrtb2.BidRequest
  3833  	}{
  3834  		{
  3835  			name:            "Empty", // Empty Request
  3836  			initialRequest:  &openrtb2.BidRequest{},
  3837  			gpp:             gpplib.GppContainer{},
  3838  			expectedRequest: &openrtb2.BidRequest{},
  3839  		},
  3840  		{
  3841  			name: "Privacy_Downgrade", // US Privacy from GPP, into empty
  3842  			initialRequest: &openrtb2.BidRequest{
  3843  				Regs: &openrtb2.Regs{
  3844  					GPPSID: []int8{6},
  3845  				},
  3846  			},
  3847  			gpp: gpplib.GppContainer{
  3848  				SectionTypes: []constants.SectionID{6},
  3849  				Sections: []gpplib.Section{
  3850  					GPPMockSection{
  3851  						sectionID: 6,
  3852  						value:     "USPrivacy",
  3853  					},
  3854  				},
  3855  			},
  3856  			expectedRequest: &openrtb2.BidRequest{
  3857  				Regs: &openrtb2.Regs{
  3858  					GPPSID:    []int8{6},
  3859  					USPrivacy: "USPrivacy",
  3860  				},
  3861  			},
  3862  		},
  3863  		{
  3864  			name: "Downgrade_Blocked_By_Existing", // US Privacy from GPP blocked by existing US Privacy
  3865  			initialRequest: &openrtb2.BidRequest{
  3866  				Regs: &openrtb2.Regs{
  3867  					GPPSID:    []int8{6},
  3868  					USPrivacy: "LegacyPrivacy",
  3869  				},
  3870  			},
  3871  			gpp: gpplib.GppContainer{
  3872  				SectionTypes: []constants.SectionID{6},
  3873  				Sections: []gpplib.Section{
  3874  					GPPMockSection{
  3875  						sectionID: 6,
  3876  						value:     "USPrivacy",
  3877  					},
  3878  				},
  3879  			},
  3880  			expectedRequest: &openrtb2.BidRequest{
  3881  				Regs: &openrtb2.Regs{
  3882  					GPPSID:    []int8{6},
  3883  					USPrivacy: "LegacyPrivacy",
  3884  				},
  3885  			},
  3886  		},
  3887  		{
  3888  			name: "No_USPrivacy", // Downgrade not possible due to missing USPrivacy
  3889  			initialRequest: &openrtb2.BidRequest{
  3890  				Regs: &openrtb2.Regs{
  3891  					GPPSID: []int8{2},
  3892  				},
  3893  			},
  3894  			gpp: gpplib.GppContainer{
  3895  				SectionTypes: []constants.SectionID{2},
  3896  				Sections: []gpplib.Section{
  3897  					GPPMockSection{
  3898  						sectionID: 2,
  3899  						value:     "GDPRConsent",
  3900  					},
  3901  				},
  3902  			},
  3903  			expectedRequest: &openrtb2.BidRequest{
  3904  				Regs: &openrtb2.Regs{
  3905  					GPPSID: []int8{2},
  3906  				},
  3907  			},
  3908  		},
  3909  		{
  3910  			name: "No_SID", // US Privacy from GPP partially blocked by no SID
  3911  			initialRequest: &openrtb2.BidRequest{
  3912  				Regs: &openrtb2.Regs{
  3913  					GPPSID: []int8{2},
  3914  				},
  3915  			},
  3916  			gpp: gpplib.GppContainer{
  3917  				SectionTypes: []constants.SectionID{2, 6},
  3918  				Sections: []gpplib.Section{
  3919  					GPPMockSection{
  3920  						sectionID: 2,
  3921  						value:     "GDPRConsent",
  3922  					},
  3923  					GPPMockSection{
  3924  						sectionID: 6,
  3925  						value:     "USPrivacy",
  3926  					},
  3927  				},
  3928  			},
  3929  			expectedRequest: &openrtb2.BidRequest{
  3930  				Regs: &openrtb2.Regs{
  3931  					GPPSID: []int8{2},
  3932  				},
  3933  			},
  3934  		},
  3935  	}
  3936  
  3937  	for _, test := range testCases {
  3938  		t.Run(test.name, func(t *testing.T) {
  3939  			setLegacyUSPFromGPP(test.initialRequest, test.gpp)
  3940  			assert.Equal(t, test.expectedRequest, test.initialRequest)
  3941  		})
  3942  	}
  3943  }
  3944  
  3945  func Test_isBidderInExtAlternateBidderCodes(t *testing.T) {
  3946  	type args struct {
  3947  		adapter               string
  3948  		currentMultiBidBidder string
  3949  		adapterABC            *openrtb_ext.ExtAlternateBidderCodes
  3950  	}
  3951  	tests := []struct {
  3952  		name string
  3953  		args args
  3954  		want bool
  3955  	}{
  3956  		{
  3957  			name: "alternatebiddercodes not defined",
  3958  			want: false,
  3959  		},
  3960  		{
  3961  			name: "adapter not defined in alternatebiddercodes",
  3962  			args: args{
  3963  				adapter: string(openrtb_ext.BidderPubmatic),
  3964  				adapterABC: &openrtb_ext.ExtAlternateBidderCodes{
  3965  					Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{string(openrtb_ext.BidderAppnexus): {}},
  3966  				},
  3967  			},
  3968  			want: false,
  3969  		},
  3970  		{
  3971  			name: "adapter defined in alternatebiddercodes but currentMultiBidBidder not in AllowedBidders list",
  3972  			args: args{
  3973  				adapter:               string(openrtb_ext.BidderPubmatic),
  3974  				currentMultiBidBidder: "groupm",
  3975  				adapterABC: &openrtb_ext.ExtAlternateBidderCodes{
  3976  					Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
  3977  						string(openrtb_ext.BidderPubmatic): {
  3978  							AllowedBidderCodes: []string{string(openrtb_ext.BidderAppnexus)},
  3979  						},
  3980  					},
  3981  				},
  3982  			},
  3983  			want: false,
  3984  		},
  3985  		{
  3986  			name: "adapter defined in alternatebiddercodes with currentMultiBidBidder mentioned in AllowedBidders list",
  3987  			args: args{
  3988  				adapter:               string(openrtb_ext.BidderPubmatic),
  3989  				currentMultiBidBidder: "groupm",
  3990  				adapterABC: &openrtb_ext.ExtAlternateBidderCodes{
  3991  					Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
  3992  						string(openrtb_ext.BidderPubmatic): {
  3993  							AllowedBidderCodes: []string{"groupm"},
  3994  						},
  3995  					},
  3996  				},
  3997  			},
  3998  			want: true,
  3999  		},
  4000  		{
  4001  			name: "adapter defined in alternatebiddercodes with AllowedBidders list as *",
  4002  			args: args{
  4003  				adapter:               string(openrtb_ext.BidderPubmatic),
  4004  				currentMultiBidBidder: "groupm",
  4005  				adapterABC: &openrtb_ext.ExtAlternateBidderCodes{
  4006  					Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
  4007  						string(openrtb_ext.BidderPubmatic): {
  4008  							AllowedBidderCodes: []string{"*"},
  4009  						},
  4010  					},
  4011  				},
  4012  			},
  4013  			want: true,
  4014  		},
  4015  	}
  4016  	for _, tt := range tests {
  4017  		t.Run(tt.name, func(t *testing.T) {
  4018  			if got := isBidderInExtAlternateBidderCodes(tt.args.adapter, tt.args.currentMultiBidBidder, tt.args.adapterABC); got != tt.want {
  4019  				t.Errorf("isBidderInExtAlternateBidderCodes() = %v, want %v", got, tt.want)
  4020  			}
  4021  		})
  4022  	}
  4023  }
  4024  
  4025  func Test_buildRequestExtMultiBid(t *testing.T) {
  4026  	type args struct {
  4027  		adapter     string
  4028  		reqMultiBid []*openrtb_ext.ExtMultiBid
  4029  		adapterABC  *openrtb_ext.ExtAlternateBidderCodes
  4030  	}
  4031  	tests := []struct {
  4032  		name string
  4033  		args args
  4034  		want []*openrtb_ext.ExtMultiBid
  4035  	}{
  4036  		{
  4037  			name: "multi-bid config not defined",
  4038  			args: args{
  4039  				adapter:     string(openrtb_ext.BidderPubmatic),
  4040  				reqMultiBid: nil,
  4041  			},
  4042  			want: nil,
  4043  		},
  4044  		{
  4045  			name: "adapter not defined in multi-bid config",
  4046  			args: args{
  4047  				adapter: string(openrtb_ext.BidderPubmatic),
  4048  				reqMultiBid: []*openrtb_ext.ExtMultiBid{
  4049  					{
  4050  						Bidder:  string(openrtb_ext.BidderAppnexus),
  4051  						MaxBids: ptrutil.ToPtr(2),
  4052  					},
  4053  				},
  4054  			},
  4055  			want: nil,
  4056  		},
  4057  		{
  4058  			name: "adapter defined in multi-bid config as Bidder object along with other bidders",
  4059  			args: args{
  4060  				adapter: string(openrtb_ext.BidderPubmatic),
  4061  				reqMultiBid: []*openrtb_ext.ExtMultiBid{
  4062  					{
  4063  						Bidder:  string(openrtb_ext.BidderAppnexus),
  4064  						MaxBids: ptrutil.ToPtr(3),
  4065  					},
  4066  					{
  4067  						Bidder:  string(openrtb_ext.BidderPubmatic),
  4068  						MaxBids: ptrutil.ToPtr(2),
  4069  					},
  4070  					{
  4071  						Bidders: []string{string(openrtb_ext.Bidder33Across), string(openrtb_ext.BidderRubicon)},
  4072  						MaxBids: ptrutil.ToPtr(2),
  4073  					},
  4074  				},
  4075  			},
  4076  			want: []*openrtb_ext.ExtMultiBid{
  4077  				{
  4078  					Bidder:  string(openrtb_ext.BidderPubmatic),
  4079  					MaxBids: ptrutil.ToPtr(2),
  4080  				},
  4081  			},
  4082  		},
  4083  		{
  4084  			name: "adapter defined in multi-bid config as a entry of Bidders list along with other bidders",
  4085  			args: args{
  4086  				adapter: string(openrtb_ext.BidderRubicon),
  4087  				reqMultiBid: []*openrtb_ext.ExtMultiBid{
  4088  					{
  4089  						Bidder:  string(openrtb_ext.BidderAppnexus),
  4090  						MaxBids: ptrutil.ToPtr(3),
  4091  					},
  4092  					{
  4093  						Bidder:  string(openrtb_ext.BidderPubmatic),
  4094  						MaxBids: ptrutil.ToPtr(2),
  4095  					},
  4096  					{
  4097  						Bidders: []string{string(openrtb_ext.Bidder33Across), string(openrtb_ext.BidderRubicon)},
  4098  						MaxBids: ptrutil.ToPtr(4),
  4099  					},
  4100  				},
  4101  			},
  4102  			want: []*openrtb_ext.ExtMultiBid{
  4103  				{
  4104  					Bidders: []string{string(openrtb_ext.BidderRubicon)},
  4105  					MaxBids: ptrutil.ToPtr(4),
  4106  				},
  4107  			},
  4108  		},
  4109  		{
  4110  			name: "adapter defined in multi-bid config as Bidder object along with other bidders with alternateBiddercode",
  4111  			args: args{
  4112  				adapter: string(openrtb_ext.BidderPubmatic),
  4113  				reqMultiBid: []*openrtb_ext.ExtMultiBid{
  4114  					{
  4115  						Bidder:  "groupm",
  4116  						MaxBids: ptrutil.ToPtr(3),
  4117  					},
  4118  					{
  4119  						Bidder:  string(openrtb_ext.BidderPubmatic),
  4120  						MaxBids: ptrutil.ToPtr(2),
  4121  					},
  4122  					{
  4123  						Bidders: []string{string(openrtb_ext.Bidder33Across), string(openrtb_ext.BidderRubicon)},
  4124  						MaxBids: ptrutil.ToPtr(2),
  4125  					},
  4126  				},
  4127  				adapterABC: &openrtb_ext.ExtAlternateBidderCodes{
  4128  					Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
  4129  						string(openrtb_ext.BidderPubmatic): {
  4130  							AllowedBidderCodes: []string{"groupm"},
  4131  						},
  4132  					},
  4133  				},
  4134  			},
  4135  			want: []*openrtb_ext.ExtMultiBid{
  4136  				{
  4137  					Bidder:  "groupm",
  4138  					MaxBids: ptrutil.ToPtr(3),
  4139  				},
  4140  				{
  4141  					Bidder:  string(openrtb_ext.BidderPubmatic),
  4142  					MaxBids: ptrutil.ToPtr(2),
  4143  				},
  4144  			},
  4145  		},
  4146  		{
  4147  			name: "adapter defined in multi-bid config as a entry of Bidders list along with other bidders with alternateBiddercode",
  4148  			args: args{
  4149  				adapter: string(openrtb_ext.BidderAppnexus),
  4150  				reqMultiBid: []*openrtb_ext.ExtMultiBid{
  4151  					{
  4152  						Bidder:  "groupm",
  4153  						MaxBids: ptrutil.ToPtr(3),
  4154  					},
  4155  					{
  4156  						Bidder:  string(openrtb_ext.BidderPubmatic),
  4157  						MaxBids: ptrutil.ToPtr(2),
  4158  					},
  4159  					{
  4160  						Bidders: []string{string(openrtb_ext.Bidder33Across), string(openrtb_ext.BidderAppnexus)},
  4161  						MaxBids: ptrutil.ToPtr(4),
  4162  					},
  4163  				},
  4164  				adapterABC: &openrtb_ext.ExtAlternateBidderCodes{
  4165  					Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
  4166  						string(openrtb_ext.BidderAppnexus): {
  4167  							AllowedBidderCodes: []string{"groupm"},
  4168  						},
  4169  					},
  4170  				},
  4171  			},
  4172  			want: []*openrtb_ext.ExtMultiBid{
  4173  				{
  4174  					Bidder:  "groupm",
  4175  					MaxBids: ptrutil.ToPtr(3),
  4176  				},
  4177  				{
  4178  					Bidders: []string{string(openrtb_ext.BidderAppnexus)},
  4179  					MaxBids: ptrutil.ToPtr(4),
  4180  				},
  4181  			},
  4182  		},
  4183  		{
  4184  			name: "adapter defined in multi-bid config as Bidder object along with other bidders with alternateBiddercode.AllowedBidders as *",
  4185  			args: args{
  4186  				adapter: string(openrtb_ext.BidderPubmatic),
  4187  				reqMultiBid: []*openrtb_ext.ExtMultiBid{
  4188  					{
  4189  						Bidder:  "groupm",
  4190  						MaxBids: ptrutil.ToPtr(3),
  4191  					},
  4192  					{
  4193  						Bidder:  string(openrtb_ext.BidderPubmatic),
  4194  						MaxBids: ptrutil.ToPtr(2),
  4195  					},
  4196  					{
  4197  						Bidders: []string{string(openrtb_ext.Bidder33Across), string(openrtb_ext.BidderRubicon)},
  4198  						MaxBids: ptrutil.ToPtr(2),
  4199  					},
  4200  				},
  4201  				adapterABC: &openrtb_ext.ExtAlternateBidderCodes{
  4202  					Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
  4203  						string(openrtb_ext.BidderPubmatic): {
  4204  							AllowedBidderCodes: []string{"*"},
  4205  						},
  4206  					},
  4207  				},
  4208  			},
  4209  			want: []*openrtb_ext.ExtMultiBid{
  4210  				{
  4211  					Bidder:  "groupm",
  4212  					MaxBids: ptrutil.ToPtr(3),
  4213  				},
  4214  				{
  4215  					Bidder:  string(openrtb_ext.BidderPubmatic),
  4216  					MaxBids: ptrutil.ToPtr(2),
  4217  				},
  4218  				{
  4219  					Bidders: []string{string(openrtb_ext.Bidder33Across)},
  4220  					MaxBids: ptrutil.ToPtr(2),
  4221  				},
  4222  				{
  4223  					Bidders: []string{string(openrtb_ext.BidderRubicon)},
  4224  					MaxBids: ptrutil.ToPtr(2),
  4225  				},
  4226  			},
  4227  		},
  4228  		{
  4229  			name: "adapter defined in multi-bid config as a entry of Bidders list along with other bidders with alternateBiddercode.AllowedBidders as *",
  4230  			args: args{
  4231  				adapter: string(openrtb_ext.BidderAppnexus),
  4232  				reqMultiBid: []*openrtb_ext.ExtMultiBid{
  4233  					{
  4234  						Bidder:  "groupm",
  4235  						MaxBids: ptrutil.ToPtr(3),
  4236  					},
  4237  					{
  4238  						Bidder:  string(openrtb_ext.BidderPubmatic),
  4239  						MaxBids: ptrutil.ToPtr(2),
  4240  					},
  4241  					{
  4242  						Bidders: []string{string(openrtb_ext.Bidder33Across), string(openrtb_ext.BidderAppnexus)},
  4243  						MaxBids: ptrutil.ToPtr(4),
  4244  					},
  4245  				},
  4246  				adapterABC: &openrtb_ext.ExtAlternateBidderCodes{
  4247  					Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
  4248  						string(openrtb_ext.BidderAppnexus): {
  4249  							AllowedBidderCodes: []string{"*"},
  4250  						},
  4251  					},
  4252  				},
  4253  			},
  4254  			want: []*openrtb_ext.ExtMultiBid{
  4255  				{
  4256  					Bidder:  "groupm",
  4257  					MaxBids: ptrutil.ToPtr(3),
  4258  				},
  4259  				{
  4260  					Bidder:  string(openrtb_ext.BidderPubmatic),
  4261  					MaxBids: ptrutil.ToPtr(2),
  4262  				},
  4263  				{
  4264  					Bidders: []string{string(openrtb_ext.Bidder33Across)},
  4265  					MaxBids: ptrutil.ToPtr(4),
  4266  				},
  4267  				{
  4268  					Bidders: []string{string(openrtb_ext.BidderAppnexus)},
  4269  					MaxBids: ptrutil.ToPtr(4),
  4270  				},
  4271  			},
  4272  		},
  4273  	}
  4274  	for _, tt := range tests {
  4275  		t.Run(tt.name, func(t *testing.T) {
  4276  			got := buildRequestExtMultiBid(tt.args.adapter, tt.args.reqMultiBid, tt.args.adapterABC)
  4277  			assert.Equal(t, tt.want, got)
  4278  		})
  4279  	}
  4280  }
  4281  
  4282  func TestGetPrebidMediaTypeForBid(t *testing.T) {
  4283  	tests := []struct {
  4284  		description     string
  4285  		inputBid        openrtb2.Bid
  4286  		expectedBidType openrtb_ext.BidType
  4287  		expectedError   string
  4288  	}{
  4289  		{
  4290  			description:     "Valid bid ext with bid type native",
  4291  			inputBid:        openrtb2.Bid{ID: "bidId", ImpID: "impId", Ext: json.RawMessage(`{"prebid": {"type": "native"}}`)},
  4292  			expectedBidType: openrtb_ext.BidTypeNative,
  4293  		},
  4294  		{
  4295  			description:   "Valid bid ext with non-existing bid type",
  4296  			inputBid:      openrtb2.Bid{ID: "bidId", ImpID: "impId", Ext: json.RawMessage(`{"prebid": {"type": "unknown"}}`)},
  4297  			expectedError: "Failed to parse bid mediatype for impression \"impId\", invalid BidType: unknown",
  4298  		},
  4299  		{
  4300  			description:   "Invalid bid ext",
  4301  			inputBid:      openrtb2.Bid{ID: "bidId", ImpID: "impId", Ext: json.RawMessage(`[true`)},
  4302  			expectedError: "Failed to parse bid mediatype for impression \"impId\", expect { or n, but found [",
  4303  		},
  4304  		{
  4305  			description:   "Bid ext is nil",
  4306  			inputBid:      openrtb2.Bid{ID: "bidId", ImpID: "impId", Ext: nil},
  4307  			expectedError: "Failed to parse bid mediatype for impression \"impId\"",
  4308  		},
  4309  		{
  4310  			description:   "Empty bid ext",
  4311  			inputBid:      openrtb2.Bid{ID: "bidId", ImpID: "impId", Ext: json.RawMessage(`{}`)},
  4312  			expectedError: "Failed to parse bid mediatype for impression \"impId\"",
  4313  		},
  4314  	}
  4315  	for _, tt := range tests {
  4316  		t.Run(tt.description, func(t *testing.T) {
  4317  			bidType, err := getPrebidMediaTypeForBid(tt.inputBid)
  4318  			if len(tt.expectedError) == 0 {
  4319  				assert.Equal(t, tt.expectedBidType, bidType)
  4320  			} else {
  4321  				assert.Equal(t, tt.expectedError, err.Error())
  4322  			}
  4323  
  4324  		})
  4325  	}
  4326  }
  4327  
  4328  func TestGetMediaTypeForBid(t *testing.T) {
  4329  	tests := []struct {
  4330  		description     string
  4331  		inputBid        openrtb2.Bid
  4332  		expectedBidType openrtb_ext.BidType
  4333  		expectedError   string
  4334  	}{
  4335  		{
  4336  			description:     "Valid bid ext with bid type native",
  4337  			inputBid:        openrtb2.Bid{ID: "bidId", ImpID: "impId", Ext: json.RawMessage(`{"prebid": {"type": "native"}}`)},
  4338  			expectedBidType: openrtb_ext.BidTypeNative,
  4339  		},
  4340  		{
  4341  			description:   "invalid bid ext",
  4342  			inputBid:      openrtb2.Bid{ID: "bidId", ImpID: "impId", Ext: json.RawMessage(`{"prebid"`)},
  4343  			expectedError: "Failed to parse bid mediatype for impression \"impId\", expect :, but found \x00",
  4344  		},
  4345  		{
  4346  			description:     "Valid bid ext with mtype native",
  4347  			inputBid:        openrtb2.Bid{ID: "bidId", ImpID: "impId", MType: openrtb2.MarkupNative},
  4348  			expectedBidType: openrtb_ext.BidTypeNative,
  4349  		},
  4350  		{
  4351  			description:     "Valid bid ext with mtype banner",
  4352  			inputBid:        openrtb2.Bid{ID: "bidId", ImpID: "impId", MType: openrtb2.MarkupBanner},
  4353  			expectedBidType: openrtb_ext.BidTypeBanner,
  4354  		},
  4355  		{
  4356  			description:     "Valid bid ext with mtype video",
  4357  			inputBid:        openrtb2.Bid{ID: "bidId", ImpID: "impId", MType: openrtb2.MarkupVideo},
  4358  			expectedBidType: openrtb_ext.BidTypeVideo,
  4359  		},
  4360  		{
  4361  			description:     "Valid bid ext with mtype audio",
  4362  			inputBid:        openrtb2.Bid{ID: "bidId", ImpID: "impId", MType: openrtb2.MarkupAudio},
  4363  			expectedBidType: openrtb_ext.BidTypeAudio,
  4364  		},
  4365  		{
  4366  			description:   "Valid bid ext with mtype unknown",
  4367  			inputBid:      openrtb2.Bid{ID: "bidId", ImpID: "impId", MType: 8},
  4368  			expectedError: "Failed to parse bid mType for impression \"impId\"",
  4369  		},
  4370  	}
  4371  	for _, tt := range tests {
  4372  		t.Run(tt.description, func(t *testing.T) {
  4373  			bidType, err := getMediaTypeForBid(tt.inputBid)
  4374  			if len(tt.expectedError) == 0 {
  4375  				assert.Equal(t, tt.expectedBidType, bidType)
  4376  			} else {
  4377  				assert.Equal(t, tt.expectedError, err.Error())
  4378  			}
  4379  
  4380  		})
  4381  	}
  4382  }
  4383  
  4384  func TestCleanOpenRTBRequestsActivities(t *testing.T) {
  4385  	expectedUserDefault := openrtb2.User{
  4386  		ID:       "our-id",
  4387  		BuyerUID: "their-id",
  4388  		Yob:      1982,
  4389  		Gender:   "test",
  4390  		Ext:      json.RawMessage(`{"data": 1, "test": 2}`),
  4391  		Geo:      &openrtb2.Geo{Lat: ptrutil.ToPtr(123.456), Lon: ptrutil.ToPtr(11.278)},
  4392  		EIDs: []openrtb2.EID{
  4393  			{Source: "eids-source"},
  4394  		},
  4395  		Data: []openrtb2.Data{{ID: "data-id"}},
  4396  	}
  4397  	expectedDeviceDefault := openrtb2.Device{
  4398  		UA:       deviceUA,
  4399  		IP:       "132.173.230.74",
  4400  		Language: "EN",
  4401  		DIDMD5:   "DIDMD5",
  4402  		IFA:      "IFA",
  4403  		DIDSHA1:  "DIDSHA1",
  4404  		DPIDMD5:  "DPIDMD5",
  4405  		DPIDSHA1: "DPIDSHA1",
  4406  		MACMD5:   "MACMD5",
  4407  		MACSHA1:  "MACSHA1",
  4408  		Geo:      &openrtb2.Geo{Lat: ptrutil.ToPtr(123.456), Lon: ptrutil.ToPtr(11.278)},
  4409  	}
  4410  
  4411  	expectedSourceDefault := openrtb2.Source{
  4412  		TID: "testTID",
  4413  	}
  4414  
  4415  	testCases := []struct {
  4416  		name              string
  4417  		req               *openrtb2.BidRequest
  4418  		privacyConfig     config.AccountPrivacy
  4419  		componentName     string
  4420  		allow             bool
  4421  		expectedReqNumber int
  4422  		expectedUser      openrtb2.User
  4423  		expectUserScrub   bool
  4424  		expectedDevice    openrtb2.Device
  4425  		expectedSource    openrtb2.Source
  4426  		expectedImpExt    json.RawMessage
  4427  	}{
  4428  		{
  4429  			name:              "fetch_bids_request_with_one_bidder_allowed",
  4430  			req:               newBidRequest(t),
  4431  			privacyConfig:     getFetchBidsActivityConfig("appnexus", true),
  4432  			expectedReqNumber: 1,
  4433  			expectedUser:      expectedUserDefault,
  4434  			expectedDevice:    expectedDeviceDefault,
  4435  			expectedSource:    expectedSourceDefault,
  4436  		},
  4437  		{
  4438  			name:              "fetch_bids_request_with_one_bidder_not_allowed",
  4439  			req:               newBidRequest(t),
  4440  			privacyConfig:     getFetchBidsActivityConfig("appnexus", false),
  4441  			expectedReqNumber: 0,
  4442  			expectedUser:      expectedUserDefault,
  4443  			expectedDevice:    expectedDeviceDefault,
  4444  			expectedSource:    expectedSourceDefault,
  4445  		},
  4446  		{
  4447  			name:              "transmit_ufpd_allowed",
  4448  			req:               newBidRequest(t),
  4449  			privacyConfig:     getTransmitUFPDActivityConfig("appnexus", true),
  4450  			expectedReqNumber: 1,
  4451  			expectedUser:      expectedUserDefault,
  4452  			expectedDevice:    expectedDeviceDefault,
  4453  			expectedSource:    expectedSourceDefault,
  4454  		},
  4455  		{
  4456  			//remove user.eids, user.ext.data.*, user.data.*, user.{id, buyeruid, yob, gender}
  4457  			//and device-specific IDs
  4458  			name:              "transmit_ufpd_deny",
  4459  			req:               newBidRequest(t),
  4460  			privacyConfig:     getTransmitUFPDActivityConfig("appnexus", false),
  4461  			expectedReqNumber: 1,
  4462  			expectedUser: openrtb2.User{
  4463  				ID:       "",
  4464  				BuyerUID: "",
  4465  				Yob:      0,
  4466  				Geo:      &openrtb2.Geo{Lat: ptrutil.ToPtr(123.456), Lon: ptrutil.ToPtr(11.278)},
  4467  				EIDs:     nil,
  4468  				Ext:      json.RawMessage(`{"test":2}`),
  4469  				Data:     nil,
  4470  			},
  4471  			expectUserScrub: true,
  4472  			expectedDevice: openrtb2.Device{
  4473  				UA:       deviceUA,
  4474  				Language: "EN",
  4475  				IP:       "132.173.230.74",
  4476  				DIDMD5:   "",
  4477  				IFA:      "",
  4478  				DIDSHA1:  "",
  4479  				DPIDMD5:  "",
  4480  				DPIDSHA1: "",
  4481  				MACMD5:   "",
  4482  				MACSHA1:  "",
  4483  				Geo:      &openrtb2.Geo{Lat: ptrutil.ToPtr(123.456), Lon: ptrutil.ToPtr(11.278)},
  4484  			},
  4485  			expectedSource: expectedSourceDefault,
  4486  		},
  4487  		{
  4488  			name:              "transmit_precise_geo_allowed",
  4489  			req:               newBidRequest(t),
  4490  			privacyConfig:     getTransmitPreciseGeoActivityConfig("appnexus", true),
  4491  			expectedReqNumber: 1,
  4492  			expectedUser:      expectedUserDefault,
  4493  			expectedDevice:    expectedDeviceDefault,
  4494  			expectedSource:    expectedSourceDefault,
  4495  		},
  4496  		{
  4497  			//round user's geographic location by rounding off IP address and lat/lng data.
  4498  			//this applies to both device.geo and user.geo
  4499  			name:              "transmit_precise_geo_deny",
  4500  			req:               newBidRequest(t),
  4501  			privacyConfig:     getTransmitPreciseGeoActivityConfig("appnexus", false),
  4502  			expectedReqNumber: 1,
  4503  			expectedUser: openrtb2.User{
  4504  				ID:       "our-id",
  4505  				BuyerUID: "their-id",
  4506  				Yob:      1982,
  4507  				Geo:      &openrtb2.Geo{Lat: ptrutil.ToPtr(123.46), Lon: ptrutil.ToPtr(11.28)},
  4508  				Gender:   "test",
  4509  				Ext:      json.RawMessage(`{"data": 1, "test": 2}`),
  4510  				EIDs: []openrtb2.EID{
  4511  					{Source: "eids-source"},
  4512  				},
  4513  				Data: []openrtb2.Data{{ID: "data-id"}},
  4514  			},
  4515  			expectedDevice: openrtb2.Device{
  4516  				UA:       deviceUA,
  4517  				IP:       "132.173.0.0",
  4518  				Language: "EN",
  4519  				DIDMD5:   "DIDMD5",
  4520  				IFA:      "IFA",
  4521  				DIDSHA1:  "DIDSHA1",
  4522  				DPIDMD5:  "DPIDMD5",
  4523  				DPIDSHA1: "DPIDSHA1",
  4524  				MACMD5:   "MACMD5",
  4525  				MACSHA1:  "MACSHA1",
  4526  				Geo:      &openrtb2.Geo{Lat: ptrutil.ToPtr(123.46), Lon: ptrutil.ToPtr(11.28)},
  4527  			},
  4528  			expectedSource: expectedSourceDefault,
  4529  		},
  4530  		{
  4531  			name:              "transmit_tid_allowed",
  4532  			req:               newBidRequest(t),
  4533  			privacyConfig:     getTransmitTIDActivityConfig("appnexus", true),
  4534  			expectedReqNumber: 1,
  4535  			expectedUser:      expectedUserDefault,
  4536  			expectedDevice:    expectedDeviceDefault,
  4537  			expectedSource:    expectedSourceDefault,
  4538  		},
  4539  		{
  4540  			//remove source.tid and imp.ext.tid
  4541  			name:              "transmit_tid_deny",
  4542  			req:               newBidRequest(t),
  4543  			privacyConfig:     getTransmitTIDActivityConfig("appnexus", false),
  4544  			expectedReqNumber: 1,
  4545  			expectedUser:      expectedUserDefault,
  4546  			expectedDevice:    expectedDeviceDefault,
  4547  			expectedSource: openrtb2.Source{
  4548  				TID: "",
  4549  			},
  4550  			expectedImpExt: json.RawMessage(`{"bidder": {"placementId": 1}}`),
  4551  		},
  4552  	}
  4553  
  4554  	for _, test := range testCases {
  4555  		t.Run(test.name, func(t *testing.T) {
  4556  			activities := privacy.NewActivityControl(&test.privacyConfig)
  4557  			auctionReq := AuctionRequest{
  4558  				BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: test.req},
  4559  				UserSyncs:         &emptyUsersync{},
  4560  				Activities:        activities,
  4561  				Account: config.Account{Privacy: config.AccountPrivacy{
  4562  					IPv6Config: config.IPv6{
  4563  						AnonKeepBits: 32,
  4564  					},
  4565  					IPv4Config: config.IPv4{
  4566  						AnonKeepBits: 16,
  4567  					},
  4568  				}},
  4569  				TCF2Config: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}),
  4570  			}
  4571  
  4572  			metricsMock := metrics.MetricsEngineMock{}
  4573  			metricsMock.Mock.On("RecordAdapterBuyerUIDScrubbed", mock.Anything).Return()
  4574  
  4575  			bidderToSyncerKey := map[string]string{}
  4576  			reqSplitter := &requestSplitter{
  4577  				bidderToSyncerKey: bidderToSyncerKey,
  4578  				me:                &metricsMock,
  4579  				hostSChainNode:    nil,
  4580  				bidderInfo:        config.BidderInfos{},
  4581  			}
  4582  
  4583  			bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, false, map[string]float64{})
  4584  			assert.Empty(t, errs)
  4585  			assert.Len(t, bidderRequests, test.expectedReqNumber)
  4586  
  4587  			if test.expectedReqNumber == 1 {
  4588  				assert.Equal(t, &test.expectedUser, bidderRequests[0].BidRequest.User)
  4589  				assert.Equal(t, &test.expectedDevice, bidderRequests[0].BidRequest.Device)
  4590  				assert.Equal(t, &test.expectedSource, bidderRequests[0].BidRequest.Source)
  4591  
  4592  				if len(test.expectedImpExt) > 0 {
  4593  					assert.JSONEq(t, string(test.expectedImpExt), string(bidderRequests[0].BidRequest.Imp[0].Ext))
  4594  				}
  4595  				if test.expectUserScrub {
  4596  					metricsMock.AssertCalled(t, "RecordAdapterBuyerUIDScrubbed", openrtb_ext.BidderAppnexus)
  4597  				} else {
  4598  					metricsMock.AssertNotCalled(t, "RecordAdapterBuyerUIDScrubbed", openrtb_ext.BidderAppnexus)
  4599  				}
  4600  			}
  4601  		})
  4602  	}
  4603  }
  4604  
  4605  func buildDefaultActivityConfig(componentName string, allow bool) config.Activity {
  4606  	return config.Activity{
  4607  		Default: ptrutil.ToPtr(true),
  4608  		Rules: []config.ActivityRule{
  4609  			{
  4610  				Allow: allow,
  4611  				Condition: config.ActivityCondition{
  4612  					ComponentName: []string{componentName},
  4613  					ComponentType: []string{"bidder"},
  4614  				},
  4615  			},
  4616  		},
  4617  	}
  4618  }
  4619  
  4620  func getFetchBidsActivityConfig(componentName string, allow bool) config.AccountPrivacy {
  4621  	return config.AccountPrivacy{
  4622  		AllowActivities: &config.AllowActivities{
  4623  			FetchBids: buildDefaultActivityConfig(componentName, allow),
  4624  		},
  4625  	}
  4626  }
  4627  
  4628  func getTransmitUFPDActivityConfig(componentName string, allow bool) config.AccountPrivacy {
  4629  	return config.AccountPrivacy{
  4630  		AllowActivities: &config.AllowActivities{
  4631  			TransmitUserFPD: buildDefaultActivityConfig(componentName, allow),
  4632  		},
  4633  	}
  4634  }
  4635  
  4636  func getTransmitPreciseGeoActivityConfig(componentName string, allow bool) config.AccountPrivacy {
  4637  	return config.AccountPrivacy{
  4638  		AllowActivities: &config.AllowActivities{
  4639  			TransmitPreciseGeo: buildDefaultActivityConfig(componentName, allow),
  4640  		},
  4641  	}
  4642  }
  4643  
  4644  func getTransmitTIDActivityConfig(componentName string, allow bool) config.AccountPrivacy {
  4645  	return config.AccountPrivacy{
  4646  		AllowActivities: &config.AllowActivities{
  4647  			TransmitTids: buildDefaultActivityConfig(componentName, allow),
  4648  		},
  4649  	}
  4650  }
  4651  
  4652  func TestApplyBidAdjustmentToFloor(t *testing.T) {
  4653  	type args struct {
  4654  		allBidderRequests    []BidderRequest
  4655  		bidAdjustmentFactors map[string]float64
  4656  	}
  4657  	tests := []struct {
  4658  		name                      string
  4659  		args                      args
  4660  		expectedAllBidderRequests []BidderRequest
  4661  	}{
  4662  		{
  4663  			name: " bidAdjustmentFactor is empty",
  4664  			args: args{
  4665  				allBidderRequests: []BidderRequest{
  4666  					{
  4667  						BidRequest: &openrtb2.BidRequest{
  4668  							Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
  4669  						},
  4670  						BidderName: openrtb_ext.BidderName("appnexus"),
  4671  					},
  4672  				},
  4673  				bidAdjustmentFactors: map[string]float64{},
  4674  			},
  4675  			expectedAllBidderRequests: []BidderRequest{
  4676  				{
  4677  					BidRequest: &openrtb2.BidRequest{
  4678  						Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
  4679  					},
  4680  					BidderName: openrtb_ext.BidderName("appnexus"),
  4681  				},
  4682  			},
  4683  		},
  4684  		{
  4685  			name: "bidAdjustmentFactor not present for request bidder",
  4686  			args: args{
  4687  				allBidderRequests: []BidderRequest{
  4688  					{
  4689  						BidRequest: &openrtb2.BidRequest{
  4690  							Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
  4691  						},
  4692  						BidderName: openrtb_ext.BidderName("appnexus"),
  4693  					},
  4694  				},
  4695  				bidAdjustmentFactors: map[string]float64{"pubmatic": 1.0},
  4696  			},
  4697  			expectedAllBidderRequests: []BidderRequest{
  4698  				{
  4699  					BidRequest: &openrtb2.BidRequest{
  4700  						Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
  4701  					},
  4702  					BidderName: openrtb_ext.BidderName("appnexus"),
  4703  				},
  4704  			},
  4705  		},
  4706  		{
  4707  			name: "bidAdjustmentFactor present for request bidder",
  4708  			args: args{
  4709  				allBidderRequests: []BidderRequest{
  4710  					{
  4711  						BidRequest: &openrtb2.BidRequest{
  4712  							Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
  4713  						},
  4714  						BidderName: openrtb_ext.BidderName("appnexus"),
  4715  					},
  4716  				},
  4717  				bidAdjustmentFactors: map[string]float64{"pubmatic": 1.0, "appnexus": 0.75},
  4718  			},
  4719  			expectedAllBidderRequests: []BidderRequest{
  4720  				{
  4721  					BidRequest: &openrtb2.BidRequest{
  4722  						Imp: []openrtb2.Imp{{BidFloor: 133.33333333333334}, {BidFloor: 200}},
  4723  					},
  4724  					BidderName: openrtb_ext.BidderName("appnexus"),
  4725  				},
  4726  			},
  4727  		},
  4728  		{
  4729  			name: "bidAdjustmentFactor present only for appnexus request bidder",
  4730  			args: args{
  4731  				allBidderRequests: []BidderRequest{
  4732  					{
  4733  						BidRequest: &openrtb2.BidRequest{
  4734  							Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
  4735  						},
  4736  						BidderName: openrtb_ext.BidderName("appnexus"),
  4737  					},
  4738  					{
  4739  						BidRequest: &openrtb2.BidRequest{
  4740  							Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
  4741  						},
  4742  						BidderName: openrtb_ext.BidderName("pubmatic"),
  4743  					},
  4744  				},
  4745  				bidAdjustmentFactors: map[string]float64{"appnexus": 0.75},
  4746  			},
  4747  			expectedAllBidderRequests: []BidderRequest{
  4748  				{
  4749  					BidRequest: &openrtb2.BidRequest{
  4750  						Imp: []openrtb2.Imp{{BidFloor: 133.33333333333334}, {BidFloor: 200}},
  4751  					},
  4752  					BidderName: openrtb_ext.BidderName("appnexus"),
  4753  				},
  4754  				{
  4755  					BidRequest: &openrtb2.BidRequest{
  4756  						Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
  4757  					},
  4758  					BidderName: openrtb_ext.BidderName("pubmatic"),
  4759  				},
  4760  			},
  4761  		},
  4762  	}
  4763  	for _, tt := range tests {
  4764  		t.Run(tt.name, func(t *testing.T) {
  4765  			applyBidAdjustmentToFloor(tt.args.allBidderRequests, tt.args.bidAdjustmentFactors)
  4766  			assert.Equal(t, tt.expectedAllBidderRequests, tt.args.allBidderRequests, tt.name)
  4767  		})
  4768  	}
  4769  }
  4770  
  4771  func TestBuildRequestExtAlternateBidderCodes(t *testing.T) {
  4772  	type testInput struct {
  4773  		bidderNameRaw string
  4774  		accABC        *openrtb_ext.ExtAlternateBidderCodes
  4775  		reqABC        *openrtb_ext.ExtAlternateBidderCodes
  4776  	}
  4777  	testCases := []struct {
  4778  		desc     string
  4779  		in       testInput
  4780  		expected *openrtb_ext.ExtAlternateBidderCodes
  4781  	}{
  4782  		{
  4783  			desc:     "No biddername, nil reqABC and accABC",
  4784  			in:       testInput{},
  4785  			expected: nil,
  4786  		},
  4787  		{
  4788  			desc: "No biddername, non-nil reqABC",
  4789  			in: testInput{
  4790  				reqABC: &openrtb_ext.ExtAlternateBidderCodes{},
  4791  			},
  4792  			expected: &openrtb_ext.ExtAlternateBidderCodes{},
  4793  		},
  4794  		{
  4795  			desc: "No biddername, non-nil accABC",
  4796  			in: testInput{
  4797  				accABC: &openrtb_ext.ExtAlternateBidderCodes{},
  4798  			},
  4799  			expected: &openrtb_ext.ExtAlternateBidderCodes{},
  4800  		},
  4801  		{
  4802  			desc: "No biddername, non-nil reqABC nor accABC",
  4803  			in: testInput{
  4804  				reqABC: &openrtb_ext.ExtAlternateBidderCodes{},
  4805  				accABC: &openrtb_ext.ExtAlternateBidderCodes{},
  4806  			},
  4807  			expected: &openrtb_ext.ExtAlternateBidderCodes{},
  4808  		},
  4809  		{
  4810  			desc: "non-nil reqABC",
  4811  			in: testInput{
  4812  				bidderNameRaw: "pubmatic",
  4813  				reqABC:        &openrtb_ext.ExtAlternateBidderCodes{},
  4814  			},
  4815  			expected: &openrtb_ext.ExtAlternateBidderCodes{},
  4816  		},
  4817  		{
  4818  			desc: "non-nil accABC",
  4819  			in: testInput{
  4820  				bidderNameRaw: "pubmatic",
  4821  				accABC:        &openrtb_ext.ExtAlternateBidderCodes{},
  4822  			},
  4823  			expected: &openrtb_ext.ExtAlternateBidderCodes{},
  4824  		},
  4825  		{
  4826  			desc: "both reqABC and accABC enabled and bidder matches elements in accABC but reqABC comes first",
  4827  			in: testInput{
  4828  				bidderNameRaw: "PUBmatic",
  4829  				reqABC: &openrtb_ext.ExtAlternateBidderCodes{
  4830  					Enabled: true,
  4831  					Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
  4832  						"appnexus": {
  4833  							AllowedBidderCodes: []string{"pubCode1"},
  4834  						},
  4835  					},
  4836  				},
  4837  				accABC: &openrtb_ext.ExtAlternateBidderCodes{
  4838  					Enabled: true,
  4839  					Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
  4840  						"PubMatic": {
  4841  							AllowedBidderCodes: []string{"pubCode2"},
  4842  						},
  4843  					},
  4844  				},
  4845  			},
  4846  			expected: &openrtb_ext.ExtAlternateBidderCodes{Enabled: true},
  4847  		},
  4848  		{
  4849  			desc: "both reqABC and accABC enabled and bidder matches elements in both but we prioritize reqABC",
  4850  			in: testInput{
  4851  				bidderNameRaw: "pubmatic",
  4852  				reqABC: &openrtb_ext.ExtAlternateBidderCodes{
  4853  					Enabled: true,
  4854  					Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
  4855  						"PubMatic": {
  4856  							AllowedBidderCodes: []string{"pubCode"},
  4857  						},
  4858  					},
  4859  				},
  4860  				accABC: &openrtb_ext.ExtAlternateBidderCodes{
  4861  					Enabled: true,
  4862  					Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
  4863  						"appnexus": {
  4864  							AllowedBidderCodes: []string{"anxsCode"},
  4865  						},
  4866  					},
  4867  				},
  4868  			},
  4869  			expected: &openrtb_ext.ExtAlternateBidderCodes{
  4870  				Enabled: true,
  4871  				Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
  4872  					"pubmatic": {
  4873  						AllowedBidderCodes: []string{"pubCode"},
  4874  					},
  4875  				},
  4876  			},
  4877  		},
  4878  		{
  4879  			desc: "nil reqABC non-nil accABC enabled and bidder matches elements in accABC",
  4880  			in: testInput{
  4881  				bidderNameRaw: "APPnexus",
  4882  				accABC: &openrtb_ext.ExtAlternateBidderCodes{
  4883  					Enabled: true,
  4884  					Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
  4885  						"appnexus": {
  4886  							AllowedBidderCodes: []string{"anxsCode"},
  4887  						},
  4888  					},
  4889  				},
  4890  			},
  4891  			expected: &openrtb_ext.ExtAlternateBidderCodes{
  4892  				Enabled: true,
  4893  				Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
  4894  					"APPnexus": {
  4895  						AllowedBidderCodes: []string{"anxsCode"},
  4896  					},
  4897  				},
  4898  			},
  4899  		},
  4900  	}
  4901  
  4902  	for _, tc := range testCases {
  4903  		t.Run(tc.desc, func(t *testing.T) {
  4904  			alternateBidderCodes := buildRequestExtAlternateBidderCodes(tc.in.bidderNameRaw, tc.in.accABC, tc.in.reqABC)
  4905  			assert.Equal(t, tc.expected, alternateBidderCodes)
  4906  		})
  4907  	}
  4908  }
  4909  
  4910  func TestCopyExtAlternateBidderCodes(t *testing.T) {
  4911  	type testInput struct {
  4912  		bidder               string
  4913  		alternateBidderCodes *openrtb_ext.ExtAlternateBidderCodes
  4914  	}
  4915  	testCases := []struct {
  4916  		desc     string
  4917  		in       testInput
  4918  		expected *openrtb_ext.ExtAlternateBidderCodes
  4919  	}{
  4920  		{
  4921  			desc:     "pass a nil alternateBidderCodes argument, expect nil output",
  4922  			in:       testInput{},
  4923  			expected: nil,
  4924  		},
  4925  		{
  4926  			desc: "non-nil alternateBidderCodes argument but bidder doesn't match",
  4927  			in: testInput{
  4928  				alternateBidderCodes: &openrtb_ext.ExtAlternateBidderCodes{
  4929  					Enabled: true,
  4930  				},
  4931  			},
  4932  			expected: &openrtb_ext.ExtAlternateBidderCodes{
  4933  				Enabled: true,
  4934  			},
  4935  		},
  4936  		{
  4937  			desc: "non-nil alternateBidderCodes argument bidder is identical to one element in map",
  4938  			in: testInput{
  4939  				bidder: "appnexus",
  4940  				alternateBidderCodes: &openrtb_ext.ExtAlternateBidderCodes{
  4941  					Enabled: true,
  4942  					Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
  4943  						"appnexus": {
  4944  							AllowedBidderCodes: []string{"adnxs"},
  4945  						},
  4946  					},
  4947  				},
  4948  			},
  4949  			expected: &openrtb_ext.ExtAlternateBidderCodes{
  4950  				Enabled: true,
  4951  				Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
  4952  					"appnexus": {
  4953  						AllowedBidderCodes: []string{"adnxs"},
  4954  					},
  4955  				},
  4956  			},
  4957  		},
  4958  		{
  4959  			desc: "case insensitive match, keep bidder casing in output",
  4960  			in: testInput{
  4961  				bidder: "AppNexus",
  4962  				alternateBidderCodes: &openrtb_ext.ExtAlternateBidderCodes{
  4963  					Enabled: true,
  4964  					Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
  4965  						"appnexus": {
  4966  							AllowedBidderCodes: []string{"adnxs"},
  4967  						},
  4968  					},
  4969  				},
  4970  			},
  4971  			expected: &openrtb_ext.ExtAlternateBidderCodes{
  4972  				Enabled: true,
  4973  				Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{
  4974  					"AppNexus": {
  4975  						AllowedBidderCodes: []string{"adnxs"},
  4976  					},
  4977  				},
  4978  			},
  4979  		},
  4980  	}
  4981  	for _, tc := range testCases {
  4982  		t.Run(tc.desc, func(t *testing.T) {
  4983  			alternateBidderCodes := copyExtAlternateBidderCodes(tc.in.bidder, tc.in.alternateBidderCodes)
  4984  			assert.Equal(t, tc.expected, alternateBidderCodes)
  4985  		})
  4986  	}
  4987  }
  4988  
  4989  func TestBuildBidResponseRequestBidderName(t *testing.T) {
  4990  	bidderImpResponses := stored_responses.BidderImpsWithBidResponses{
  4991  		openrtb_ext.BidderName("appnexus"): {"impId1": json.RawMessage(`{}`), "impId2": json.RawMessage(`{}`)},
  4992  		openrtb_ext.BidderName("appneXUS"): {"impId3": json.RawMessage(`{}`), "impId4": json.RawMessage(`{}`)},
  4993  	}
  4994  
  4995  	bidderImpReplaceImpID := stored_responses.BidderImpReplaceImpID{
  4996  		"appnexus": {"impId1": true, "impId2": false},
  4997  		"appneXUS": {"impId3": true, "impId4": false},
  4998  	}
  4999  	result := buildBidResponseRequest(nil, bidderImpResponses, nil, bidderImpReplaceImpID)
  5000  
  5001  	resultAppnexus := result["appnexus"]
  5002  	assert.Equal(t, resultAppnexus.BidderName, openrtb_ext.BidderName("appnexus"))
  5003  	assert.Equal(t, resultAppnexus.ImpReplaceImpId, map[string]bool{"impId1": true, "impId2": false})
  5004  
  5005  	resultAppneXUS := result["appneXUS"]
  5006  	assert.Equal(t, resultAppneXUS.BidderName, openrtb_ext.BidderName("appneXUS"))
  5007  	assert.Equal(t, resultAppneXUS.ImpReplaceImpId, map[string]bool{"impId3": true, "impId4": false})
  5008  
  5009  }