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